diff --git a/.drone.yml b/.drone.yml index ac25d7f66..b1f475952 100644 --- a/.drone.yml +++ b/.drone.yml @@ -29,7 +29,7 @@ steps: - cd /drone/build - useradd -m -s /bin/bash test - chown -R test:test . - - su -c 'ASAN_OPTIONS=detect_leaks=0 xvfb-run ctest --output-on-failure' test + - su -c 'ASAN_OPTIONS=detect_odr_violation=0,detect_leaks=0 xvfb-run ctest --output-on-failure' test services: - name: server @@ -97,7 +97,7 @@ steps: - cd /drone/build - useradd -m -s /bin/bash test - chown -R test:test . - - su -c 'ASAN_OPTIONS=detect_leaks=0 xvfb-run ctest --output-on-failure' test + - su -c 'ASAN_OPTIONS=detect_odr_violation=0,detect_leaks=0 xvfb-run ctest --output-on-failure' test services: - name: server diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 88378c1f5..b179cddf4 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -8,7 +8,7 @@ on: jobs: build: name: Build - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-11 env: SONAR_SERVER_URL: "https://sonarcloud.io" diff --git a/src/common/pinstate.h b/src/common/pinstate.h index 106d9385d..7d4222a01 100644 --- a/src/common/pinstate.h +++ b/src/common/pinstate.h @@ -21,7 +21,6 @@ namespace OCC { -namespace PinStateEnums { OCSYNC_EXPORT Q_NAMESPACE /** Determines whether items should be available locally permanently or not @@ -126,8 +125,6 @@ enum class VfsItemAvailability { OnlineOnly = 4, }; Q_ENUM_NS(VfsItemAvailability) -} -using namespace PinStateEnums; } diff --git a/src/gui/editlocallyjob.cpp b/src/gui/editlocallyjob.cpp index 78aedd34f..526d76ccc 100644 --- a/src/gui/editlocallyjob.cpp +++ b/src/gui/editlocallyjob.cpp @@ -438,7 +438,7 @@ void EditLocallyJob::showErrorNotification(const QString &message, const QString }); if (foundFolder != folderMap.cend()) { - emit (*foundFolder)->syncEngine().addErrorToGui(SyncFileItem::SoftError, message, informativeText); + emit (*foundFolder)->syncEngine().addErrorToGui(SyncFileItem::SoftError, message, informativeText, OCC::ErrorCategory::GenericError); } } diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index b16d0daf6..31542c6bc 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -890,7 +890,7 @@ void Folder::startSync(const QStringList &pathList) _fileLog->start(path()); if (!reloadExcludes()) { - slotSyncError(tr("Could not read system exclude file")); + slotSyncError(tr("Could not read system exclude file"), ErrorCategory::GenericError); QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection, Q_ARG(bool, false)); return; } @@ -1005,9 +1005,9 @@ void Folder::slotSyncError(const QString &message, ErrorCategory category) } } -void Folder::slotAddErrorToGui(SyncFileItem::Status status, const QString &errorMessage, const QString &subject) +void Folder::slotAddErrorToGui(SyncFileItem::Status status, const QString &errorMessage, const QString &subject, ErrorCategory category) { - emit ProgressDispatcher::instance()->addErrorToGui(alias(), status, errorMessage, subject); + emit ProgressDispatcher::instance()->addErrorToGui(alias(), status, errorMessage, subject, category); } void Folder::slotSyncStarted() @@ -1128,7 +1128,7 @@ void Folder::slotTransmissionProgress(const ProgressInfo &pi) } // a item is completed: count the errors and forward to the ProgressDispatcher -void Folder::slotItemCompleted(const SyncFileItemPtr &item) +void Folder::slotItemCompleted(const SyncFileItemPtr &item, ErrorCategory errorCategory) { if (item->_instruction == CSYNC_INSTRUCTION_NONE || item->_instruction == CSYNC_INSTRUCTION_UPDATE_METADATA) { // We only care about the updates that deserve to be shown in the UI @@ -1144,7 +1144,7 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item) _syncResult.processCompletedItem(item); _fileLog->logItem(*item); - emit ProgressDispatcher::instance()->itemCompleted(alias(), item); + emit ProgressDispatcher::instance()->itemCompleted(alias(), item, errorCategory); } void Folder::slotNewBigFolderDiscovered(const QString &newF, bool isExternal) diff --git a/src/gui/folder.h b/src/gui/folder.h index 6f26eb5e5..9d654baed 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -388,12 +388,12 @@ private slots: /** Adds a error message that's not tied to a specific item. */ - void slotSyncError(const QString &message, OCC::ErrorCategory category = OCC::ErrorCategory::Normal); + void slotSyncError(const QString &message, OCC::ErrorCategory category); - void slotAddErrorToGui(OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject = {}); + void slotAddErrorToGui(OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject, OCC::ErrorCategory category); void slotTransmissionProgress(const OCC::ProgressInfo &pi); - void slotItemCompleted(const OCC::SyncFileItemPtr &); + void slotItemCompleted(const OCC::SyncFileItemPtr &, OCC::ErrorCategory errorCategory); void slotRunEtagJob(); void etagRetrieved(const QByteArray &, const QDateTime &tp); diff --git a/src/gui/tray/ActivityItemContent.qml b/src/gui/tray/ActivityItemContent.qml index 81823e23e..b5a55f7c6 100644 --- a/src/gui/tray/ActivityItemContent.qml +++ b/src/gui/tray/ActivityItemContent.qml @@ -221,7 +221,7 @@ RowLayout { Layout.fillHeight: true Layout.alignment: Qt.AlignTop | Qt.AlignLeft - text: (root.activityData.type === "Sync") ? root.activityData.displayPath + text: (root.activityData.type === "Sync") ? root.activityData.subject : (root.activityData.type === "File") ? root.activityData.subject : (root.activityData.type === "Notification") ? root.activityData.message : "" diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index f36b7b974..a917a580a 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -556,11 +556,15 @@ void ActivityListModel::addEntriesToActivityList(const ActivityList &activityLis setHasSyncConflicts(conflictsFound); } -void ActivityListModel::addErrorToActivityList(const Activity &activity) +void ActivityListModel::addErrorToActivityList(const Activity &activity, const ErrorType type) { - qCInfo(lcActivity) << "Error successfully added to the notification list: " << activity._message << activity._subject << activity._syncResultStatus << activity._syncFileItemStatus; - addEntriesToActivityList({activity}); - _notificationErrorsLists.prepend(activity); + qCInfo(lcActivity) << "Error successfully added to the notification list: " << type << activity._message << activity._subject << activity._syncResultStatus << activity._syncFileItemStatus; + auto modifiedActivity = activity; + if (type == ErrorType::NetworkError) { + modifiedActivity._subject = tr("Network error occurred: client will retry syncing."); + } + addEntriesToActivityList({modifiedActivity}); + _notificationErrorsLists.prepend(modifiedActivity); } void ActivityListModel::addIgnoredFileToList(const Activity &newActivity) diff --git a/src/gui/tray/activitylistmodel.h b/src/gui/tray/activitylistmodel.h index ce8364c17..ba75bb5c6 100644 --- a/src/gui/tray/activitylistmodel.h +++ b/src/gui/tray/activitylistmodel.h @@ -82,6 +82,12 @@ public: }; Q_ENUM(DataRole) + enum class ErrorType { + SyncError, + NetworkError, + }; + Q_ENUM(ErrorType) + explicit ActivityListModel(QObject *parent = nullptr); explicit ActivityListModel(AccountState *accountState, @@ -122,7 +128,7 @@ public slots: void slotTriggerDismiss(const int activityIndex); void addNotificationToActivityList(const OCC::Activity &activity); - void addErrorToActivityList(const OCC::Activity &activity); + void addErrorToActivityList(const OCC::Activity &activity, const ErrorType type); void addIgnoredFileToList(const OCC::Activity &newActivity); void addSyncFileItemToActivityList(const OCC::Activity &activity); void removeActivityFromActivityList(int row); diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index f3dc30c08..d56f23f15 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -627,12 +627,27 @@ void User::slotAddError(const QString &folderAlias, const QString &message, Erro activity._links.append(link); } + auto errorType = ActivityListModel::ErrorType::SyncError; // add 'other errors' to activity list - _activityModel->addErrorToActivityList(activity); + switch (category) { + case ErrorCategory::GenericError: + errorType = ActivityListModel::ErrorType::SyncError; + break; + case ErrorCategory::InsufficientRemoteStorage: + errorType = ActivityListModel::ErrorType::SyncError; + break; + case ErrorCategory::NetworkError: + errorType = ActivityListModel::ErrorType::NetworkError; + break; + case ErrorCategory::NoError: + break; + } + + _activityModel->addErrorToActivityList(activity, errorType); } } -void User::slotAddErrorToGui(const QString &folderAlias, SyncFileItem::Status status, const QString &errorMessage, const QString &subject) +void User::slotAddErrorToGui(const QString &folderAlias, const SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const ErrorCategory category) { const auto folderInstance = FolderMan::instance()->folder(folderAlias); if (!folderInstance) { @@ -668,7 +683,24 @@ void User::slotAddErrorToGui(const QString &folderAlias, SyncFileItem::Status st activity._id = -static_cast(qHash(activity._subject + activity._message)); // add 'other errors' to activity list - _activityModel->addErrorToActivityList(activity); + auto errorType = ActivityListModel::ErrorType::SyncError; + switch (category) + { + case ErrorCategory::GenericError: + errorType = ActivityListModel::ErrorType::SyncError; + break; + case ErrorCategory::InsufficientRemoteStorage: + errorType = ActivityListModel::ErrorType::SyncError; + break; + case ErrorCategory::NetworkError: + errorType = ActivityListModel::ErrorType::NetworkError; + break; + case ErrorCategory::NoError: + errorType = {}; + break; + } + + _activityModel->addErrorToActivityList(activity, errorType); showDesktopNotification(activity); @@ -787,7 +819,7 @@ void User::processCompletedSyncItem(const Folder *folder, const SyncFileItemPtr activity._links = {buttonActivityLink}; } - _activityModel->addErrorToActivityList(activity); + _activityModel->addErrorToActivityList(activity, ActivityListModel::ErrorType::SyncError); } } } diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index e17059969..353035fb6 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -117,7 +117,7 @@ public slots: void slotItemCompleted(const QString &folder, const OCC::SyncFileItemPtr &item); void slotProgressInfo(const QString &folder, const OCC::ProgressInfo &progress); void slotAddError(const QString &folderAlias, const QString &message, OCC::ErrorCategory category); - void slotAddErrorToGui(const QString &folderAlias, OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject = {}); + void slotAddErrorToGui(const QString &folderAlias, const OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const ErrorCategory category); void slotNotificationRequestFinished(int statusCode); void slotNotifyNetworkError(QNetworkReply *reply); void slotEndNotificationRequest(int replyCode); diff --git a/src/libsync/bulkpropagatorjob.cpp b/src/libsync/bulkpropagatorjob.cpp index d6e4fe9ef..698962e5a 100644 --- a/src/libsync/bulkpropagatorjob.cpp +++ b/src/libsync/bulkpropagatorjob.cpp @@ -120,7 +120,7 @@ void BulkPropagatorJob::startUploadFile(SyncFileItemPtr item, UploadFileInfo fil // Check if the specific file can be accessed if (propagator()->hasCaseClashAccessibilityProblem(fileToUpload._file)) { - done(item, SyncFileItem::NormalError, tr("File %1 cannot be uploaded because another file with the same name, differing only in case, exists").arg(QDir::toNativeSeparators(item->_file))); + done(item, SyncFileItem::NormalError, tr("File %1 cannot be uploaded because another file with the same name, differing only in case, exists").arg(QDir::toNativeSeparators(item->_file)), ErrorCategory::GenericError); return; } @@ -160,7 +160,7 @@ void BulkPropagatorJob::doStartUpload(SyncFileItemPtr item, const auto renameSuccess = QFile::rename(originalFilePathAbsolute, newFilePathAbsolute); if (!renameSuccess) { - done(item, SyncFileItem::NormalError, "File contains trailing spaces and couldn't be renamed"); + done(item, SyncFileItem::NormalError, "File contains trailing spaces and couldn't be renamed", ErrorCategory::GenericError); return; } @@ -172,7 +172,7 @@ void BulkPropagatorJob::doStartUpload(SyncFileItemPtr item, item->_modtime = FileSystem::getModTime(newFilePathAbsolute); if (item->_modtime <= 0) { _pendingChecksumFiles.remove(item->_file); - slotOnErrorStartFolderUnlock(item, SyncFileItem::NormalError, tr("File %1 has invalid modified time. Do not upload to the server.").arg(QDir::toNativeSeparators(item->_file))); + slotOnErrorStartFolderUnlock(item, SyncFileItem::NormalError, tr("File %1 has invalid modified time. Do not upload to the server.").arg(QDir::toNativeSeparators(item->_file)), ErrorCategory::GenericError); checkPropagationIsDone(); return; } @@ -293,7 +293,7 @@ void BulkPropagatorJob::slotStartUpload(SyncFileItemPtr item, if (!FileSystem::fileExists(fullFilePath)) { _pendingChecksumFiles.remove(item->_file); - slotOnErrorStartFolderUnlock(item, SyncFileItem::SoftError, tr("File Removed (start upload) %1").arg(fullFilePath)); + slotOnErrorStartFolderUnlock(item, SyncFileItem::SoftError, tr("File Removed (start upload) %1").arg(fullFilePath), ErrorCategory::GenericError); checkPropagationIsDone(); return; } @@ -305,7 +305,7 @@ void BulkPropagatorJob::slotStartUpload(SyncFileItemPtr item, item->_modtime = FileSystem::getModTime(originalFilePath); if (item->_modtime <= 0) { _pendingChecksumFiles.remove(item->_file); - slotOnErrorStartFolderUnlock(item, SyncFileItem::NormalError, tr("File %1 has invalid modification time. Do not upload to the server.").arg(QDir::toNativeSeparators(item->_file))); + slotOnErrorStartFolderUnlock(item, SyncFileItem::NormalError, tr("File %1 has invalid modification time. Do not upload to the server.").arg(QDir::toNativeSeparators(item->_file)), ErrorCategory::GenericError); checkPropagationIsDone(); return; } @@ -317,7 +317,7 @@ void BulkPropagatorJob::slotStartUpload(SyncFileItemPtr item, << "prevModtime" << prevModtime << "Curr" << item->_modtime; - slotOnErrorStartFolderUnlock(item, SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed.")); + slotOnErrorStartFolderUnlock(item, SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed."), ErrorCategory::GenericError); checkPropagationIsDone(); return; } @@ -331,7 +331,7 @@ void BulkPropagatorJob::slotStartUpload(SyncFileItemPtr item, if (fileIsStillChanging(*item)) { propagator()->_anotherSyncNeeded = true; _pendingChecksumFiles.remove(item->_file); - slotOnErrorStartFolderUnlock(item, SyncFileItem::SoftError, tr("Local file changed during sync.")); + slotOnErrorStartFolderUnlock(item, SyncFileItem::SoftError, tr("Local file changed during sync."), ErrorCategory::GenericError); checkPropagationIsDone(); return; } @@ -340,11 +340,12 @@ void BulkPropagatorJob::slotStartUpload(SyncFileItemPtr item, } void BulkPropagatorJob::slotOnErrorStartFolderUnlock(SyncFileItemPtr item, - SyncFileItem::Status status, - const QString &errorString) + const SyncFileItem::Status status, + const QString &errorString, + const ErrorCategory errorCategory) { - qCInfo(lcBulkPropagatorJob()) << status << errorString; - done(item, status, errorString); + qCInfo(lcBulkPropagatorJob()) << status << errorString << errorCategory; + done(item, status, errorString, errorCategory); } void BulkPropagatorJob::slotPutFinishedOneFile(const BulkUploadItem &singleFile, @@ -473,10 +474,10 @@ void BulkPropagatorJob::finalizeOneFile(const BulkUploadItem &oneFile) // Update the database entry const auto result = propagator()->updateMetadata(*oneFile._item); if (!result) { - done(oneFile._item, SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error())); + done(oneFile._item, SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error()), ErrorCategory::GenericError); return; } else if (*result == Vfs::ConvertToPlaceholderResult::Locked) { - done(oneFile._item, SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(oneFile._item->_file)); + done(oneFile._item, SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(oneFile._item->_file), ErrorCategory::GenericError); return; } @@ -511,7 +512,7 @@ void BulkPropagatorJob::finalize(const QJsonObject &fullReply) finalizeOneFile(singleFile); } - done(singleFile._item, singleFile._item->_status, {}); + done(singleFile._item, singleFile._item->_status, {}, ErrorCategory::GenericError); singleFileIt = _filesToUpload.erase(singleFileIt); } @@ -520,8 +521,9 @@ void BulkPropagatorJob::finalize(const QJsonObject &fullReply) } void BulkPropagatorJob::done(SyncFileItemPtr item, - SyncFileItem::Status status, - const QString &errorString) + const SyncFileItem::Status status, + const QString &errorString, + const ErrorCategory category) { item->_status = status; item->_errorString = errorString; @@ -548,7 +550,7 @@ void BulkPropagatorJob::done(SyncFileItemPtr item, handleJobDoneErrors(item, status); - emit propagator()->itemCompleted(item); + emit propagator()->itemCompleted(item, category); } QMap BulkPropagatorJob::headers(SyncFileItemPtr item) const @@ -606,7 +608,7 @@ void BulkPropagatorJob::abortWithError(SyncFileItemPtr item, const QString &error) { abort(AbortType::Synchronous); - done(item, status, error); + done(item, status, error, ErrorCategory::GenericError); } void BulkPropagatorJob::checkResettingErrors(SyncFileItemPtr item) const diff --git a/src/libsync/bulkpropagatorjob.h b/src/libsync/bulkpropagatorjob.h index 451456e97..34441d324 100644 --- a/src/libsync/bulkpropagatorjob.h +++ b/src/libsync/bulkpropagatorjob.h @@ -81,8 +81,9 @@ private slots: // invoked on internal error to unlock a folder and faile void slotOnErrorStartFolderUnlock(OCC::SyncFileItemPtr item, - OCC::SyncFileItem::Status status, - const QString &errorString); + const OCC::SyncFileItem::Status status, + const QString &errorString, + const OCC::ErrorCategory errorCategory); void slotPutFinished(); @@ -107,8 +108,9 @@ private: const QJsonObject &fullReplyObject); void done(SyncFileItemPtr item, - SyncFileItem::Status status, - const QString &errorString); + const SyncFileItem::Status status, + const QString &errorString, + const ErrorCategory category); /** Bases headers that need to be sent on the PUT, or in the MOVE for chunking-ng */ [[nodiscard]] QMap headers(SyncFileItemPtr item) const; diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 75912f4db..81e180934 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -18,6 +18,7 @@ #include "common/syncjournaldb.h" #include "filesystem.h" #include "syncfileitem.h" +#include "progressdispatcher.h" #include #include #include @@ -964,7 +965,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( // Not locally, not on the server. The entry is stale! qCInfo(lcDisco) << "Stale DB entry"; if (!_discoveryData->_statedb->deleteFileRecord(path._original, true)) { - emit _discoveryData->fatalError(tr("Error while deleting file record %1 from the database").arg(path._original)); + emit _discoveryData->fatalError(tr("Error while deleting file record %1 from the database").arg(path._original), ErrorCategory::GenericError); qCWarning(lcDisco) << "Failed to delete a file record from the local DB" << path._original; } return; @@ -1178,7 +1179,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( // either correct availability, or a result with error if the folder is new or otherwise has no availability set yet const auto folderPlaceHolderAvailability = localEntry.isDirectory ? _discoveryData->_syncOptions._vfs->availability(path._local) : Vfs::AvailabilityResult(Vfs::AvailabilityError::NoSuchItem); - const auto folderPinState = localEntry.isDirectory ? _discoveryData->_syncOptions._vfs->pinState(path._local) : Optional(PinState::Unspecified); + const auto folderPinState = localEntry.isDirectory ? _discoveryData->_syncOptions._vfs->pinState(path._local) : Optional(PinState::Unspecified); if (!isFilePlaceHolder && !folderPlaceHolderAvailability.isValid() && !folderPinState.isValid()) { // not a file placeholder and not a synced folder placeholder (new local folder) @@ -1214,10 +1215,10 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( if (isFolderPinStateOnlineOnly && folderPinState.isValid()) { qCInfo(lcDisco) << "*folderPinState:" << *folderPinState; } - emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading a folder. It's going to get cleared!"), path._local); + emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading a folder. It's going to get cleared!"), path._local, ErrorCategory::GenericError); } else { qCInfo(lcDisco) << "Wiping virtual file without db entry for" << path._local; - emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading a file. It's going to get removed!"), path._local); + emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading a file. It's going to get removed!"), path._local, ErrorCategory::GenericError); } item->_instruction = CSYNC_INSTRUCTION_REMOVE; item->_direction = SyncFileItem::Down; @@ -1813,7 +1814,7 @@ int ProcessDirectoryJob::processSubJobs(int nbJobs) void ProcessDirectoryJob::dbError() { - emit _discoveryData->fatalError(tr("Error while reading the database")); + emit _discoveryData->fatalError(tr("Error while reading the database"), ErrorCategory::GenericError); } void ProcessDirectoryJob::addVirtualFileSuffix(QString &str) const @@ -1880,7 +1881,7 @@ DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery() } else { // Fatal for the root job since it has no SyncFileItem, or for the network errors emit _discoveryData->fatalError(tr("Server replied with an error while reading directory \"%1\" : %2") - .arg(_currentFolder._server, results.error().message)); + .arg(_currentFolder._server, results.error().message), ErrorCategory::NetworkError); } } }); @@ -1910,7 +1911,7 @@ void ProcessDirectoryJob::startAsyncLocalQuery() if (_serverJob) _serverJob->abort(); - emit _discoveryData->fatalError(msg); + emit _discoveryData->fatalError(msg, ErrorCategory::NetworkError); }); connect(localJob, &DiscoverySingleLocalDirectoryJob::finishedNonFatalError, this, [this](const QString &msg) { @@ -1923,7 +1924,7 @@ void ProcessDirectoryJob::startAsyncLocalQuery() emit this->finished(); } else { // Fatal for the root job since it has no SyncFileItem - emit _discoveryData->fatalError(msg); + emit _discoveryData->fatalError(msg, ErrorCategory::GenericError); } }); diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index bc83426e0..6a60fd1c6 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -15,6 +15,7 @@ #include "discoveryphase.h" #include "discovery.h" #include "helpers.h" +#include "progressdispatcher.h" #include "account.h" #include "clientsideencryptionjobs.h" @@ -187,8 +188,8 @@ QPair DiscoveryPhase::findAndCancelDeletedJob(const QString &o qCWarning(lcDiscovery) << "(*it)->_type" << (*it)->_type; qCWarning(lcDiscovery) << "(*it)->_isRestoration " << (*it)->_isRestoration; Q_ASSERT(false); - emit addErrorToGui(SyncFileItem::Status::FatalError, tr("Error while canceling deletion of a file"), originalPath); - emit fatalError(tr("Error while canceling deletion of %1").arg(originalPath)); + emit addErrorToGui(SyncFileItem::Status::FatalError, tr("Error while canceling deletion of a file"), originalPath, ErrorCategory::GenericError); + emit fatalError(tr("Error while canceling deletion of %1").arg(originalPath), ErrorCategory::GenericError); } (*it)->_instruction = CSYNC_INSTRUCTION_NONE; result = true; diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index c833fd719..c0da6be82 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -51,6 +51,8 @@ class Account; class SyncJournalDb; class ProcessDirectoryJob; +enum class ErrorCategory; + /** * Represent all the meta-data about a file in the server */ @@ -307,7 +309,7 @@ public: QStringList _listExclusiveFiles; signals: - void fatalError(const QString &errorString); + void fatalError(const QString &errorString, const OCC::ErrorCategory errorCategory); void itemDiscovered(const OCC::SyncFileItemPtr &item); void finished(); @@ -320,7 +322,7 @@ signals: */ void silentlyExcluded(const QString &folderPath); - void addErrorToGui(SyncFileItem::Status status, const QString &errorMessage, const QString &subject); + void addErrorToGui(const SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const OCC::ErrorCategory category); }; /// Implementation of DiscoveryPhase::adjustRenamedPath diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 19d5e6191..31efa1af2 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -221,7 +221,7 @@ void blacklistUpdate(SyncJournalDb *journal, SyncFileItem &item) } } -void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &errorString) +void PropagateItemJob::done(const SyncFileItem::Status statusArg, const QString &errorString, const ErrorCategory category) { // Duplicate calls to done() are a logic error ENFORCE(_state != Finished); @@ -283,7 +283,7 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error qCWarning(lcPropagator) << "Could not complete propagation of" << _item->destination() << "by" << this << "with status" << _item->_status << "and error:" << _item->_errorString; else qCInfo(lcPropagator) << "Completed propagation of" << _item->destination() << "by" << this << "with status" << _item->_status; - emit propagator()->itemCompleted(_item); + emit propagator()->itemCompleted(_item, category); emit finished(_item->_status); if (_item->_status == SyncFileItem::FatalError) { @@ -302,9 +302,9 @@ void PropagateItemJob::slotRestoreJobFinished(SyncFileItem::Status status) if (status == SyncFileItem::Success || status == SyncFileItem::Conflict || status == SyncFileItem::Restoration) { - done(SyncFileItem::SoftError, msg); + done(SyncFileItem::SoftError, msg, ErrorCategory::GenericError); } else { - done(status, tr("A file or folder was removed from a read only share, but restoring failed: %1").arg(msg)); + done(status, tr("A file or folder was removed from a read only share, but restoring failed: %1").arg(msg), ErrorCategory::GenericError); } } @@ -661,7 +661,7 @@ void OwncloudPropagator::startDirectoryPropagation(const SyncFileItemPtr &item, item->_instruction = CSyncEnums::CSYNC_INSTRUCTION_ERROR; item->_errorString = tr("Error with the metadata. Getting unexpected metadata format."); item->_status = SyncFileItem::NormalError; - emit itemCompleted(item); + emit itemCompleted(item, OCC::ErrorCategory::GenericError); } else { directoryPropagationJob->appendJob(new UpdateFileDropMetadataJob(this, item->_file)); item->_instruction = CSYNC_INSTRUCTION_NONE; @@ -1087,6 +1087,56 @@ OwncloudPropagator *PropagatorJob::propagator() const return qobject_cast(parent()); } +ErrorCategory PropagatorJob::errorCategoryFromNetworkError(const QNetworkReply::NetworkError error) +{ + auto result = ErrorCategory::NoError; + switch (error) + { + case QNetworkReply::NoError: + result = ErrorCategory::NoError; + break; + case QNetworkReply::TemporaryNetworkFailureError: + case QNetworkReply::RemoteHostClosedError: + result = ErrorCategory::NetworkError; + break; + case QNetworkReply::ConnectionRefusedError: + case QNetworkReply::HostNotFoundError: + case QNetworkReply::TimeoutError: + case QNetworkReply::OperationCanceledError: + case QNetworkReply::SslHandshakeFailedError: + case QNetworkReply::NetworkSessionFailedError: + case QNetworkReply::BackgroundRequestNotAllowedError: + case QNetworkReply::TooManyRedirectsError: + case QNetworkReply::InsecureRedirectError: + case QNetworkReply::UnknownNetworkError: + case QNetworkReply::ProxyConnectionRefusedError: + case QNetworkReply::ProxyConnectionClosedError: + case QNetworkReply::ProxyNotFoundError: + case QNetworkReply::ProxyTimeoutError: + case QNetworkReply::ProxyAuthenticationRequiredError: + case QNetworkReply::UnknownProxyError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ContentNotFoundError: + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentReSendError: + case QNetworkReply::ContentConflictError: + case QNetworkReply::ContentGoneError: + case QNetworkReply::UnknownContentError: + case QNetworkReply::ProtocolUnknownError: + case QNetworkReply::ProtocolInvalidOperationError: + case QNetworkReply::ProtocolFailure: + case QNetworkReply::InternalServerError: + case QNetworkReply::OperationNotImplementedError: + case QNetworkReply::ServiceUnavailableError: + case QNetworkReply::UnknownServerError: + result = ErrorCategory::GenericError; + break; + } + + return result; +} + // ================================================================================ PropagatorJob::JobParallelism PropagatorCompositeJob::parallelism() const @@ -1521,7 +1571,7 @@ void CleanupPollsJob::slotPollFinished() auto *job = qobject_cast(sender()); ASSERT(job); if (job->_item->_status == SyncFileItem::FatalError) { - emit aborted(job->_item->_errorString); + emit aborted(job->_item->_errorString, ErrorCategory::GenericError); deleteLater(); return; } else if (job->_item->_status != SyncFileItem::Success) { @@ -1531,7 +1581,7 @@ void CleanupPollsJob::slotPollFinished() qCWarning(lcCleanupPolls) << "database error"; job->_item->_status = SyncFileItem::FatalError; job->_item->_errorString = tr("Error writing metadata to the database"); - emit aborted(job->_item->_errorString); + emit aborted(job->_item->_errorString, ErrorCategory::GenericError); deleteLater(); return; } @@ -1554,7 +1604,7 @@ QString OwncloudPropagator::remotePath() const void PropagateIgnoreJob::start() { - SyncFileItem::Status status = _item->_status; + auto status = _item->_status; if (status == SyncFileItem::NoStatus) { if (_item->_instruction == CSYNC_INSTRUCTION_ERROR) { status = SyncFileItem::NormalError; @@ -1568,7 +1618,7 @@ void PropagateIgnoreJob::start() _item->_file = conflictRecord.initialBasePath; } } - done(status, _item->_errorString); + done(status, _item->_errorString, ErrorCategory::NoError); } } diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index dd0525d22..c42254241 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "csync.h" #include "syncfileitem.h" @@ -30,6 +31,7 @@ #include "bandwidthmanager.h" #include "accountfwd.h" #include "syncoptions.h" +#include "progressdispatcher.h" #include @@ -147,6 +149,8 @@ signals: protected: [[nodiscard]] OwncloudPropagator *propagator() const; + static ErrorCategory errorCategoryFromNetworkError(const QNetworkReply::NetworkError error); + /** If this job gets added to a composite job, this will point to the parent. * * For the PropagateDirectory::_firstJob it will point to @@ -165,7 +169,7 @@ class PropagateItemJob : public PropagatorJob { Q_OBJECT protected: - virtual void done(SyncFileItem::Status status, const QString &errorString = QString()); + virtual void done(const SyncFileItem::Status status, const QString &errorString, const ErrorCategory category); /* * set a custom restore job message that is used if the restore job succeeded. @@ -640,7 +644,7 @@ private slots: signals: void newItem(const OCC::SyncFileItemPtr &); - void itemCompleted(const OCC::SyncFileItemPtr &); + void itemCompleted(const SyncFileItemPtr &item, OCC::ErrorCategory category); void progress(const OCC::SyncFileItem &, qint64 bytes); void finished(bool success); @@ -721,7 +725,7 @@ public: void start(); signals: void finished(); - void aborted(const QString &error); + void aborted(const QString &error, const ErrorCategory errorCategory); private slots: void slotPollFinished(); }; diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h index 4280edcd9..5447810ee 100644 --- a/src/libsync/progressdispatcher.h +++ b/src/libsync/progressdispatcher.h @@ -27,6 +27,8 @@ namespace OCC { +OCSYNC_EXPORT Q_NAMESPACE + /** * @brief The ProgressInfo class * @ingroup libsync @@ -249,9 +251,12 @@ namespace Progress { * in IssuesWidget. */ enum class ErrorCategory { - Normal, + NoError, + GenericError, + NetworkError, InsufficientRemoteStorage, }; +Q_ENUM_NS(OCC::ErrorCategory) /** * @file progressdispatcher.h @@ -283,7 +288,7 @@ signals: /** * @brief: the item was completed by a job */ - void itemCompleted(const QString &folder, const OCC::SyncFileItemPtr &item); + void itemCompleted(const QString &folder, const OCC::SyncFileItemPtr &item, const OCC::ErrorCategory category); /** * @brief A new folder-wide sync error was seen. @@ -297,7 +302,7 @@ signals: * @param[out] full error message * @param[out] subject (optional) */ - void addErrorToGui(const QString &folder, OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject); + void addErrorToGui(const QString &folder, const OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const OCC::ErrorCategory category); /** * @brief Emitted for a folder when a sync is done, listing all pending conflicts diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index 970c8e246..b82f80a7c 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -461,7 +461,7 @@ void PropagateDownloadFile::start() SyncJournalFileRecord parentRec; if (!propagator()->_journal->getFileRecord(parentPath, &parentRec)) { qCWarning(lcPropagateDownload) << "could not get file from local DB" << parentPath; - done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(parentPath)); + done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(parentPath), ErrorCategory::GenericError); return; } @@ -478,7 +478,7 @@ void PropagateDownloadFile::start() }); connect(_downloadEncryptedHelper, &PropagateDownloadEncrypted::failed, [this] { done(SyncFileItem::NormalError, - tr("File %1 cannot be downloaded because encryption information is missing.").arg(QDir::toNativeSeparators(_item->_file))); + tr("File %1 cannot be downloaded because encryption information is missing.").arg(QDir::toNativeSeparators(_item->_file)), ErrorCategory::GenericError); }); _downloadEncryptedHelper->start(); } @@ -496,20 +496,20 @@ void PropagateDownloadFile::startAfterIsEncryptedIsChecked() QString fsPath = propagator()->fullLocalPath(_item->_file); if (!FileSystem::verifyFileUnchanged(fsPath, _item->_previousSize, _item->_previousModtime)) { propagator()->_anotherSyncNeeded = true; - done(SyncFileItem::SoftError, tr("File has changed since discovery")); + done(SyncFileItem::SoftError, tr("File has changed since discovery"), ErrorCategory::GenericError); return; } qCDebug(lcPropagateDownload) << "dehydrating file" << _item->_file; auto r = vfs->dehydratePlaceholder(*_item); if (!r) { - done(SyncFileItem::NormalError, r.error()); + done(SyncFileItem::NormalError, r.error(), ErrorCategory::GenericError); return; } if (!propagator()->_journal->deleteFileRecord(_item->_originalFile)) { qCWarning(lcPropagateDownload) << "could not delete file from local DB" << _item->_originalFile; - done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile)); + done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile), ErrorCategory::GenericError); return; } @@ -530,12 +530,12 @@ void PropagateDownloadFile::startAfterIsEncryptedIsChecked() qCDebug(lcPropagateDownload) << "creating virtual file" << _item->_file; // do a klaas' case clash check. if (propagator()->localFileNameClash(_item->_file)) { - done(SyncFileItem::FileNameClash, tr("File %1 can not be downloaded because of a local file name clash!").arg(QDir::toNativeSeparators(_item->_file))); + done(SyncFileItem::FileNameClash, tr("File %1 can not be downloaded because of a local file name clash!").arg(QDir::toNativeSeparators(_item->_file)), ErrorCategory::GenericError); return; } auto r = vfs->createPlaceholder(*_item); if (!r) { - done(SyncFileItem::NormalError, r.error()); + done(SyncFileItem::NormalError, r.error(), ErrorCategory::GenericError); return; } updateMetadata(false); @@ -675,7 +675,7 @@ void PropagateDownloadFile::startDownload() FileSystem::setFileReadOnly(_tmpFile.fileName(), false); if (!_tmpFile.open(QIODevice::Append | QIODevice::Unbuffered)) { qCWarning(lcPropagateDownload) << "could not open temporary file" << _tmpFile.fileName(); - done(SyncFileItem::NormalError, _tmpFile.errorString()); + done(SyncFileItem::NormalError, _tmpFile.errorString(), ErrorCategory::GenericError); return; } // Hide temporary after creation @@ -689,11 +689,11 @@ void PropagateDownloadFile::startDownload() // tab: instead we'll generate a general "disk space low" message and show // these detail errors only in the error view. done(SyncFileItem::DetailError, - tr("The download would reduce free local disk space below the limit")); + tr("The download would reduce free local disk space below the limit"), ErrorCategory::GenericError); emit propagator()->insufficientLocalStorage(); } else if (diskSpaceResult == OwncloudPropagator::DiskSpaceCritical) { done(SyncFileItem::FatalError, - tr("Free space on disk is less than %1").arg(Utility::octetsToString(criticalFreeSpaceLimit()))); + tr("Free space on disk is less than %1").arg(Utility::octetsToString(criticalFreeSpaceLimit())), ErrorCategory::GenericError); } // Remove the temporary, if empty. @@ -832,7 +832,7 @@ void PropagateDownloadFile::slotGetFinished() &propagator()->_anotherSyncNeeded, errorBody); } - done(status, errorString); + done(status, errorString, errorCategoryFromNetworkError(err)); return; } @@ -880,14 +880,14 @@ void PropagateDownloadFile::slotGetFinished() // This happened when trying to resume a file. The Content-Range header was files, Content-Length was == 0 qCDebug(lcPropagateDownload) << bodySize << _item->_size << _tmpFile.size() << job->resumeStart(); FileSystem::remove(_tmpFile.fileName()); - done(SyncFileItem::SoftError, QLatin1String("Broken webserver returning empty content length for non-empty file on resume")); + done(SyncFileItem::SoftError, QLatin1String("Broken webserver returning empty content length for non-empty file on resume"), ErrorCategory::GenericError); return; } if (bodySize > 0 && bodySize != _tmpFile.size() - job->resumeStart()) { qCDebug(lcPropagateDownload) << bodySize << _tmpFile.size() << job->resumeStart(); propagator()->_anotherSyncNeeded = true; - done(SyncFileItem::SoftError, tr("The file could not be downloaded completely.")); + done(SyncFileItem::SoftError, tr("The file could not be downloaded completely."), ErrorCategory::GenericError); return; } @@ -895,7 +895,7 @@ void PropagateDownloadFile::slotGetFinished() FileSystem::remove(_tmpFile.fileName()); done(SyncFileItem::NormalError, tr("The downloaded file is empty, but the server said it should have been %1.") - .arg(Utility::octetsToString(_item->_size))); + .arg(Utility::octetsToString(_item->_size)), ErrorCategory::GenericError); return; } @@ -984,7 +984,7 @@ void PropagateDownloadFile::checksumValidateFailedAbortDownload(const QString &e { FileSystem::remove(_tmpFile.fileName()); propagator()->_anotherSyncNeeded = true; - done(SyncFileItem::SoftError, errMsg); // tr("The file downloaded with a broken checksum, will be redownloaded.")); + done(SyncFileItem::SoftError, errMsg, ErrorCategory::GenericError); // tr("The file downloaded with a broken checksum, will be redownloaded.")); } void PropagateDownloadFile::deleteExistingFolder() @@ -1005,7 +1005,7 @@ void PropagateDownloadFile::deleteExistingFolder() QString error; if (!propagator()->createConflict(_item, _associatedComposite, &error)) { - done(SyncFileItem::NormalError, error); + done(SyncFileItem::NormalError, error, ErrorCategory::GenericError); } } @@ -1141,7 +1141,7 @@ void PropagateDownloadFile::finalizeDownload() if (_downloadEncryptedHelper->decryptFile(_tmpFile)) { downloadFinished(); } else { - done(SyncFileItem::NormalError, _downloadEncryptedHelper->errorString()); + done(SyncFileItem::NormalError, _downloadEncryptedHelper->errorString(), ErrorCategory::GenericError); } } else { downloadFinished(); @@ -1155,7 +1155,7 @@ void PropagateDownloadFile::downloadFinished() if (_item->_modtime <= 0) { FileSystem::remove(_tmpFile.fileName()); - done(SyncFileItem::NormalError, tr("File %1 has invalid modified time reported by server. Do not save it.").arg(QDir::toNativeSeparators(_item->_file))); + done(SyncFileItem::NormalError, tr("File %1 has invalid modified time reported by server. Do not save it.").arg(QDir::toNativeSeparators(_item->_file)), ErrorCategory::GenericError); return; } Q_ASSERT(_item->_modtime > 0); @@ -1168,7 +1168,7 @@ void PropagateDownloadFile::downloadFinished() _item->_modtime = FileSystem::getModTime(_tmpFile.fileName()); if (_item->_modtime <= 0) { FileSystem::remove(_tmpFile.fileName()); - done(SyncFileItem::NormalError, tr("File %1 has invalid modified time reported by server. Do not save it.").arg(QDir::toNativeSeparators(_item->_file))); + done(SyncFileItem::NormalError, tr("File %1 has invalid modified time reported by server. Do not save it.").arg(QDir::toNativeSeparators(_item->_file)), ErrorCategory::GenericError); return; } Q_ASSERT(_item->_modtime > 0); @@ -1193,7 +1193,7 @@ void PropagateDownloadFile::downloadFinished() // Make the file a hydrated placeholder if possible const auto result = propagator()->syncOptions()._vfs->convertToPlaceholder(_tmpFile.fileName(), *_item, filename); if (!result) { - done(SyncFileItem::NormalError, result.error()); + done(SyncFileItem::NormalError, result.error(), ErrorCategory::GenericError); return; } } @@ -1210,15 +1210,15 @@ void PropagateDownloadFile::downloadFinished() qCInfo(lcPropagateDownload) << "downloading case clashed file" << _item->_file; const auto caseClashConflictResult = propagator()->createCaseClashConflict(_item, _tmpFile.fileName()); if (caseClashConflictResult) { - done(SyncFileItem::SoftError, *caseClashConflictResult); + done(SyncFileItem::SoftError, *caseClashConflictResult, ErrorCategory::GenericError); } else { - done(SyncFileItem::FileNameClash, tr("File %1 downloaded but it resulted in a local file name clash!").arg(QDir::toNativeSeparators(_item->_file))); + done(SyncFileItem::FileNameClash, tr("File %1 downloaded but it resulted in a local file name clash!").arg(QDir::toNativeSeparators(_item->_file)), ErrorCategory::GenericError); } return; } else { QString error; if (!propagator()->createConflict(_item, _associatedComposite, &error)) { - done(SyncFileItem::SoftError, error); + done(SyncFileItem::SoftError, error, ErrorCategory::GenericError); } else { previousFileExists = false; } @@ -1240,7 +1240,7 @@ void PropagateDownloadFile::downloadFinished() const time_t expectedMtime = _item->_previousModtime; if (!FileSystem::verifyFileUnchanged(filename, expectedSize, expectedMtime)) { propagator()->_anotherSyncNeeded = true; - done(SyncFileItem::SoftError, tr("File has changed since discovery")); + done(SyncFileItem::SoftError, tr("File has changed since discovery"), ErrorCategory::GenericError); return; } } @@ -1258,7 +1258,7 @@ void PropagateDownloadFile::downloadFinished() propagator()->_anotherSyncNeeded = true; } - done(SyncFileItem::SoftError, error); + done(SyncFileItem::SoftError, error, ErrorCategory::GenericError); return; } @@ -1290,7 +1290,7 @@ void PropagateDownloadFile::downloadFinished() if (!propagator()->_journal->deleteFileRecord(virtualFile)) { qCWarning(lcPropagateDownload) << "could not delete file from local DB" << virtualFile; - done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(virtualFile)); + done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(virtualFile), ErrorCategory::GenericError); return; } @@ -1322,10 +1322,10 @@ void PropagateDownloadFile::updateMetadata(bool isConflict) const QString fn = propagator()->fullLocalPath(_item->_file); const auto result = propagator()->updateMetadata(*_item); if (!result) { - done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error())); + done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error()), ErrorCategory::GenericError); return; } else if (*result == Vfs::ConvertToPlaceholderResult::Locked) { - done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(_item->_file)); + done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(_item->_file), ErrorCategory::GenericError); return; } @@ -1337,7 +1337,7 @@ void PropagateDownloadFile::updateMetadata(bool isConflict) propagator()->_journal->commit("download file start2"); - done(isConflict ? SyncFileItem::Conflict : SyncFileItem::Success); + done(isConflict ? SyncFileItem::Conflict : SyncFileItem::Success, {}, ErrorCategory::NoError); // handle the special recall file if (!_item->_remotePerm.hasPermission(RemotePermissions::IsShared) diff --git a/src/libsync/propagateremotedelete.cpp b/src/libsync/propagateremotedelete.cpp index 84aab6fe4..ede4f07b4 100644 --- a/src/libsync/propagateremotedelete.cpp +++ b/src/libsync/propagateremotedelete.cpp @@ -45,9 +45,9 @@ void PropagateRemoteDelete::start() if (_deleteEncryptedHelper->networkError() != QNetworkReply::NoError && _deleteEncryptedHelper->networkError() != QNetworkReply::ContentNotFoundError) { status = classifyError(_deleteEncryptedHelper->networkError(), _item->_httpErrorCode, &propagator()->_anotherSyncNeeded); } - done(status, _deleteEncryptedHelper->errorString()); + done(status, _deleteEncryptedHelper->errorString(), ErrorCategory::GenericError); } else { - done(SyncFileItem::Success); + done(SyncFileItem::Success, {}, ErrorCategory::NoError); } }); _deleteEncryptedHelper->start(); @@ -94,7 +94,8 @@ void PropagateRemoteDelete::slotDeleteJobFinished() if (err != QNetworkReply::NoError && err != QNetworkReply::ContentNotFoundError) { SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode, &propagator()->_anotherSyncNeeded); - done(status, _job->errorString()); + + done(status, _job->errorString(), errorCategoryFromNetworkError(err)); return; } @@ -109,18 +110,18 @@ void PropagateRemoteDelete::slotDeleteJobFinished() done(SyncFileItem::NormalError, tr("Wrong HTTP code returned by server. Expected 204, but received \"%1 %2\".") .arg(_item->_httpErrorCode) - .arg(_job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString())); + .arg(_job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()), ErrorCategory::GenericError); return; } if (!propagator()->_journal->deleteFileRecord(_item->_originalFile, _item->isDirectory())) { qCWarning(lcPropagateRemoteDelete) << "could not delete file from local DB" << _item->_originalFile; - done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile)); + done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile), ErrorCategory::GenericError); return; } propagator()->_journal->commit("Remote Remove"); - done(SyncFileItem::Success); + done(SyncFileItem::Success, {}, ErrorCategory::NoError); } } diff --git a/src/libsync/propagateremotemkdir.cpp b/src/libsync/propagateremotemkdir.cpp index 62bd3b344..805b7dc8e 100644 --- a/src/libsync/propagateremotemkdir.cpp +++ b/src/libsync/propagateremotemkdir.cpp @@ -123,7 +123,7 @@ void PropagateRemoteMkdir::finalizeMkColJob(QNetworkReply::NetworkError err, con } else if (err != QNetworkReply::NoError) { SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode, &propagator()->_anotherSyncNeeded); - done(status, _item->_errorString); + done(status, _item->_errorString, errorCategoryFromNetworkError(err)); return; } else if (_item->_httpErrorCode != 201) { // Normally we expect "201 Created" @@ -132,7 +132,7 @@ void PropagateRemoteMkdir::finalizeMkColJob(QNetworkReply::NetworkError err, con done(SyncFileItem::NormalError, tr("Wrong HTTP code returned by server. Expected 201, but received \"%1 %2\".") .arg(_item->_httpErrorCode) - .arg(jobHttpReasonPhraseString)); + .arg(jobHttpReasonPhraseString), ErrorCategory::GenericError); return; } @@ -161,10 +161,10 @@ void PropagateRemoteMkdir::finalizeMkColJob(QNetworkReply::NetworkError err, con job->start(); } }); - connect(propfindJob, &PropfindJob::finishedWithError, this, [this]{ - // ignore the PROPFIND error + connect(propfindJob, &PropfindJob::finishedWithError, this, [this] (QNetworkReply *reply) { + const auto err = reply ? reply->error() : QNetworkReply::NetworkError::UnknownNetworkError; propagator()->_activeJobList.removeOne(this); - done(SyncFileItem::NormalError); + done(SyncFileItem::NormalError, {}, errorCategoryFromNetworkError(err)); }); propfindJob->start(); } @@ -176,7 +176,7 @@ void PropagateRemoteMkdir::slotMkdir() const auto targetFile = propagator()->fullLocalPath(_item->_renameTarget); QString renameError; if (!FileSystem::rename(existingFile, targetFile, &renameError)) { - done(SyncFileItem::NormalError, renameError); + done(SyncFileItem::NormalError, renameError, ErrorCategory::GenericError); return; } emit propagator()->touchedFile(existingFile); @@ -190,7 +190,7 @@ void PropagateRemoteMkdir::slotMkdir() SyncJournalFileRecord parentRec; bool ok = propagator()->_journal->getFileRecord(parentPath, &parentRec); if (!ok) { - done(SyncFileItem::NormalError); + done(SyncFileItem::NormalError, {}, ErrorCategory::GenericError); return; } @@ -257,13 +257,13 @@ void PropagateRemoteMkdir::success() // save the file id already so we can detect rename or remove const auto result = propagator()->updateMetadata(itemCopy); if (!result) { - done(SyncFileItem::FatalError, tr("Error writing metadata to the database: %1").arg(result.error())); + done(SyncFileItem::FatalError, tr("Error writing metadata to the database: %1").arg(result.error()), ErrorCategory::GenericError); return; } else if (*result == Vfs::ConvertToPlaceholderResult::Locked) { - done(SyncFileItem::FatalError, tr("The file %1 is currently in use").arg(_item->_file)); + done(SyncFileItem::FatalError, tr("The file %1 is currently in use").arg(_item->_file), ErrorCategory::GenericError); return; } - done(SyncFileItem::Success); + done(SyncFileItem::Success, {}, ErrorCategory::NoError); } } diff --git a/src/libsync/propagateremotemove.cpp b/src/libsync/propagateremotemove.cpp index 64ad05641..09cb179d3 100644 --- a/src/libsync/propagateremotemove.cpp +++ b/src/libsync/propagateremotemove.cpp @@ -98,7 +98,7 @@ void PropagateRemoteMove::start() SyncJournalFileRecord parentRec; bool ok = propagator()->_journal->getFileRecord(parentPath, &parentRec); if (!ok) { - done(SyncFileItem::NormalError); + done(SyncFileItem::NormalError, {}, ErrorCategory::GenericError); return; } @@ -167,7 +167,7 @@ void PropagateRemoteMove::start() QString error; if (!FileSystem::uncheckedRenameReplace(localTargetAlt, localTarget, &error)) { done(SyncFileItem::NormalError, tr("Could not rename %1 to %2, error: %3") - .arg(folderTargetAlt, folderTarget, error)); + .arg(folderTargetAlt, folderTarget, error), ErrorCategory::GenericError); return; } qCInfo(lcPropagateRemoteMove) << "Suffix vfs required local rename of" @@ -217,7 +217,7 @@ void PropagateRemoteMove::slotMoveJobFinished() << "Could not MOVE file" << filePathOriginal << " to" << filePath << " with error:" << _job->errorString() << " and successfully restored it."; } - done(status, _job->errorString()); + done(status, _job->errorString(), ErrorCategory::GenericError); return; } @@ -228,7 +228,7 @@ void PropagateRemoteMove::slotMoveJobFinished() done(SyncFileItem::NormalError, tr("Wrong HTTP code returned by server. Expected 201, but received \"%1 %2\".") .arg(_item->_httpErrorCode) - .arg(_job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString())); + .arg(_job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()), ErrorCategory::GenericError); return; } @@ -245,7 +245,7 @@ void PropagateRemoteMove::finalize() SyncJournalFileRecord oldRecord; if (!propagator()->_journal->getFileRecord(_item->_originalFile, &oldRecord)) { qCWarning(lcPropagateRemoteMove) << "could not get file from local DB" << _item->_originalFile; - done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(_item->_originalFile)); + done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(_item->_originalFile), ErrorCategory::GenericError); return; } auto &vfs = propagator()->syncOptions()._vfs; @@ -257,7 +257,7 @@ void PropagateRemoteMove::finalize() // Delete old db data. if (!propagator()->_journal->deleteFileRecord(_item->_originalFile)) { qCWarning(lcPropagateRemoteMove) << "could not delete file from local DB" << _item->_originalFile; - done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile)); + done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile), ErrorCategory::GenericError); return; } if (!vfs->setPinState(_item->_originalFile, PinState::Inherited)) { @@ -279,34 +279,34 @@ void PropagateRemoteMove::finalize() if (!QFileInfo::exists(targetFile)) { propagator()->_journal->commit("Remote Rename"); - done(SyncFileItem::Success); + done(SyncFileItem::Success, {}, ErrorCategory::NoError); return; } const auto result = propagator()->updateMetadata(newItem); if (!result) { - done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error())); + done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error()), ErrorCategory::GenericError); return; } else if (*result == Vfs::ConvertToPlaceholderResult::Locked) { - done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(newItem._file)); + done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(newItem._file), ErrorCategory::GenericError); return; } if (pinState && *pinState != PinState::Inherited && !vfs->setPinState(newItem._renameTarget, *pinState)) { - done(SyncFileItem::NormalError, tr("Error setting pin state")); + done(SyncFileItem::NormalError, tr("Error setting pin state"), ErrorCategory::GenericError); return; } if (_item->isDirectory()) { propagator()->_renamedDirectories.insert(_item->_file, _item->_renameTarget); if (!adjustSelectiveSync(propagator()->_journal, _item->_file, _item->_renameTarget)) { - done(SyncFileItem::FatalError, tr("Error writing metadata to the database")); + done(SyncFileItem::FatalError, tr("Error writing metadata to the database"), ErrorCategory::GenericError); return; } } propagator()->_journal->commit("Remote Rename"); - done(SyncFileItem::Success); + done(SyncFileItem::Success, {}, ErrorCategory::NoError); } bool PropagateRemoteMove::adjustSelectiveSync(SyncJournalDb *journal, const QString &from_, const QString &to_) diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index 6f70ce5db..76e164b6e 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -629,10 +629,10 @@ void PropagateUploadFileCommon::slotPollFinished() finalize(); } -void PropagateUploadFileCommon::done(SyncFileItem::Status status, const QString &errorString) +void PropagateUploadFileCommon::done(SyncFileItem::Status status, const QString &errorString, const ErrorCategory category) { _finished = true; - PropagateItemJob::done(status, errorString); + PropagateItemJob::done(status, errorString, category); } void PropagateUploadFileCommon::checkResettingErrors() diff --git a/src/libsync/propagateupload.h b/src/libsync/propagateupload.h index 2340108e4..535255b38 100644 --- a/src/libsync/propagateupload.h +++ b/src/libsync/propagateupload.h @@ -281,7 +281,7 @@ private slots: void slotPollFinished(); protected: - void done(SyncFileItem::Status status, const QString &errorString = QString()) override; + void done(SyncFileItem::Status status, const QString &errorString = QString(), const ErrorCategory category = ErrorCategory::NoError) override; /** * Aborts all running network jobs, except for the ones that mayAbortJob diff --git a/src/libsync/propagatorjobs.cpp b/src/libsync/propagatorjobs.cpp index 84005cdb9..9749f5a1d 100644 --- a/src/libsync/propagatorjobs.cpp +++ b/src/libsync/propagatorjobs.cpp @@ -102,7 +102,7 @@ void PropagateLocalRemove::start() qCInfo(lcPropagateLocalRemove) << "Going to delete:" << filename; if (propagator()->localFileNameClash(_item->_file)) { - done(SyncFileItem::FileNameClash, tr("Could not remove %1 because of a local file name clash").arg(QDir::toNativeSeparators(filename))); + done(SyncFileItem::FileNameClash, tr("Could not remove %1 because of a local file name clash").arg(QDir::toNativeSeparators(filename)), ErrorCategory::GenericError); return; } @@ -110,19 +110,19 @@ void PropagateLocalRemove::start() if (_moveToTrash) { if ((QDir(filename).exists() || FileSystem::fileExists(filename)) && !FileSystem::moveToTrash(filename, &removeError)) { - done(SyncFileItem::NormalError, removeError); + done(SyncFileItem::NormalError, removeError, ErrorCategory::GenericError); return; } } else { if (_item->isDirectory()) { if (QDir(filename).exists() && !removeRecursively(QString())) { - done(SyncFileItem::NormalError, _error); + done(SyncFileItem::NormalError, _error, ErrorCategory::GenericError); return; } } else { if (FileSystem::fileExists(filename) && !FileSystem::remove(filename, &removeError)) { - done(SyncFileItem::NormalError, removeError); + done(SyncFileItem::NormalError, removeError, ErrorCategory::GenericError); return; } } @@ -130,11 +130,11 @@ void PropagateLocalRemove::start() propagator()->reportProgress(*_item, 0); if (!propagator()->_journal->deleteFileRecord(_item->_originalFile, _item->isDirectory())) { qCWarning(lcPropagateLocalRename) << "could not delete file from local DB" << _item->_originalFile; - done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile)); + done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(_item->_originalFile), ErrorCategory::GenericError); return; } propagator()->_journal->commit("Local remove"); - done(SyncFileItem::Success); + done(SyncFileItem::Success, {}, ErrorCategory::NoError); } void PropagateLocalMkdir::start() @@ -164,13 +164,13 @@ void PropagateLocalMkdir::startLocalMkdir() if (!FileSystem::remove(newDirStr, &removeError)) { done(SyncFileItem::NormalError, tr("could not delete file %1, error: %2") - .arg(newDirStr, removeError)); + .arg(newDirStr, removeError), ErrorCategory::GenericError); return; } } else if (_item->_instruction == CSYNC_INSTRUCTION_CONFLICT) { QString error; if (!propagator()->createConflict(_item, _associatedComposite, &error)) { - done(SyncFileItem::SoftError, error); + done(SyncFileItem::SoftError, error, ErrorCategory::GenericError); return; } } @@ -178,13 +178,13 @@ void PropagateLocalMkdir::startLocalMkdir() if (Utility::fsCasePreserving() && propagator()->localFileNameClash(_item->_file)) { qCWarning(lcPropagateLocalMkdir) << "New folder to create locally already exists with different case:" << _item->_file; - done(SyncFileItem::FileNameClash, tr("Folder %1 cannot be created because of a local file or folder name clash!").arg(newDirStr)); + done(SyncFileItem::FileNameClash, tr("Folder %1 cannot be created because of a local file or folder name clash!").arg(newDirStr), ErrorCategory::GenericError); return; } emit propagator()->touchedFile(newDirStr); QDir localDir(propagator()->localPath()); if (!localDir.mkpath(_item->_file)) { - done(SyncFileItem::NormalError, tr("Could not create folder %1").arg(newDirStr)); + done(SyncFileItem::NormalError, tr("Could not create folder %1").arg(newDirStr), ErrorCategory::GenericError); return; } @@ -197,10 +197,10 @@ void PropagateLocalMkdir::startLocalMkdir() newItem._etag = "_invalid_"; const auto result = propagator()->updateMetadata(newItem); if (!result) { - done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error())); + done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error()), ErrorCategory::GenericError); return; } else if (*result == Vfs::ConvertToPlaceholderResult::Locked) { - done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(newItem._file)); + done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(newItem._file), ErrorCategory::GenericError); return; } propagator()->_journal->commit("localMkdir"); @@ -208,7 +208,7 @@ void PropagateLocalMkdir::startLocalMkdir() auto resultStatus = _item->_instruction == CSYNC_INSTRUCTION_CONFLICT ? SyncFileItem::Conflict : SyncFileItem::Success; - done(resultStatus); + done(resultStatus, {}, ErrorCategory::NoError); } PropagateLocalRename::PropagateLocalRename(OwncloudPropagator *propagator, const SyncFileItemPtr &item) @@ -249,9 +249,9 @@ void PropagateLocalRename::start() qCInfo(lcPropagateLocalRename) << "renaming a case clashed file" << _item->_file << _item->_renameTarget; const auto caseClashConflictResult = propagator()->createCaseClashConflict(_item, existingFile); if (caseClashConflictResult) { - done(SyncFileItem::SoftError, *caseClashConflictResult); + done(SyncFileItem::SoftError, *caseClashConflictResult, ErrorCategory::GenericError); } else { - done(SyncFileItem::FileNameClash, tr("File %1 downloaded but it resulted in a local file name clash!").arg(QDir::toNativeSeparators(_item->_file))); + done(SyncFileItem::FileNameClash, tr("File %1 downloaded but it resulted in a local file name clash!").arg(QDir::toNativeSeparators(_item->_file)), ErrorCategory::GenericError); } return; } @@ -259,7 +259,7 @@ void PropagateLocalRename::start() emit propagator()->touchedFile(existingFile); emit propagator()->touchedFile(targetFile); if (QString renameError; !FileSystem::rename(existingFile, targetFile, &renameError)) { - done(SyncFileItem::NormalError, renameError); + done(SyncFileItem::NormalError, renameError, ErrorCategory::GenericError); return; } } @@ -267,19 +267,20 @@ void PropagateLocalRename::start() SyncJournalFileRecord oldRecord; if (!propagator()->_journal->getFileRecord(fileAlreadyMoved ? previousNameInDb : _item->_originalFile, &oldRecord)) { qCWarning(lcPropagateLocalRename) << "could not get file from local DB" << _item->_originalFile; - done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(_item->_originalFile)); + done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(_item->_originalFile), ErrorCategory::GenericError); return; } if (fileAlreadyMoved && !deleteOldDbRecord(previousNameInDb)) { return; } else if (!deleteOldDbRecord(_item->_originalFile)) { + qCWarning(lcPropagateLocalRename) << "could not delete file from local DB" << _item->_originalFile; return; } if (!vfs->setPinState(_item->_renameTarget, pinState)) { qCWarning(lcPropagateLocalRename) << "Could not set pin state of" << _item->_renameTarget << "to old value" << pinState; - done(SyncFileItem::NormalError, tr("Error setting pin state")); + done(SyncFileItem::NormalError, tr("Error setting pin state"), ErrorCategory::GenericError); return; } @@ -292,10 +293,10 @@ void PropagateLocalRename::start() } const auto result = propagator()->updateMetadata(newItem); if (!result) { - done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error())); + done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error()), ErrorCategory::GenericError); return; } else if (*result == Vfs::ConvertToPlaceholderResult::Locked) { - done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(newItem._file)); + done(SyncFileItem::SoftError, tr("The file %1 is currently in use").arg(newItem._file), ErrorCategory::GenericError); return; } } else { @@ -312,12 +313,12 @@ void PropagateLocalRename::start() SyncJournalFileRecord oldRecord; if (!propagator()->_journal->getFileRecord(oldFileName, &oldRecord)) { qCWarning(lcPropagateLocalRename) << "could not get file from local DB" << oldFileName; - done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(oldFileNameString)); + done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(oldFileNameString), OCC::ErrorCategory::GenericError); return; } if (!propagator()->_journal->deleteFileRecord(oldFileName)) { qCWarning(lcPropagateLocalRename) << "could not delete file from local DB" << oldFileName; - done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(oldFileNameString)); + done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(oldFileNameString), OCC::ErrorCategory::GenericError); return; } @@ -325,36 +326,40 @@ void PropagateLocalRename::start() newItem->_file = newFileNameString; const auto result = propagator()->updateMetadata(*newItem); if (!result) { - done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error())); + done(SyncFileItem::FatalError, tr("Error updating metadata: %1").arg(result.error()), OCC::ErrorCategory::GenericError); return; } }); if (!dbQueryResult) { - done(SyncFileItem::FatalError, tr("Failed to propagate directory rename in hierarchy")); + done(SyncFileItem::FatalError, tr("Failed to propagate directory rename in hierarchy"), OCC::ErrorCategory::GenericError); return; } propagator()->_renamedDirectories.insert(oldFile, _item->_renameTarget); if (!PropagateRemoteMove::adjustSelectiveSync(propagator()->_journal, oldFile, _item->_renameTarget)) { - done(SyncFileItem::FatalError, tr("Failed to rename file")); + done(SyncFileItem::FatalError, tr("Failed to rename file"), ErrorCategory::GenericError); return; } } + if (pinState != PinState::Inherited && !vfs->setPinState(_item->_renameTarget, pinState)) { + done(SyncFileItem::NormalError, tr("Error setting pin state"), ErrorCategory::GenericError); + return; + } propagator()->_journal->commit("localRename"); - done(SyncFileItem::Success); + done(SyncFileItem::Success, {}, ErrorCategory::NoError); } bool PropagateLocalRename::deleteOldDbRecord(const QString &fileName) { if (SyncJournalFileRecord oldRecord; !propagator()->_journal->getFileRecord(fileName, &oldRecord)) { qCWarning(lcPropagateLocalRename) << "could not get file from local DB" << fileName; - done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(fileName)); + done(SyncFileItem::NormalError, tr("could not get file %1 from local DB").arg(fileName), OCC::ErrorCategory::GenericError); return false; } if (!propagator()->_journal->deleteFileRecord(fileName)) { qCWarning(lcPropagateLocalRename) << "could not delete file from local DB" << fileName; - done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(fileName)); + done(SyncFileItem::NormalError, tr("Could not delete file record %1 from local DB").arg(fileName), OCC::ErrorCategory::GenericError); return false; } diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index e8ec169d7..f162b0ed5 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -379,7 +379,7 @@ void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item) item->_status = SyncFileItem::Status::NormalError; item->_instruction = CSYNC_INSTRUCTION_ERROR; item->_errorString = tr("Could not update file: %1").arg(result.error()); - emit itemCompleted(item); + emit itemCompleted(item, ErrorCategory::GenericError); return; } } @@ -391,14 +391,14 @@ void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item) item->_status = SyncFileItem::Status::NormalError; item->_instruction = CSYNC_INSTRUCTION_ERROR; item->_errorString = tr("Could not update virtual file metadata: %1").arg(r.error()); - emit itemCompleted(item); + emit itemCompleted(item, ErrorCategory::GenericError); return; } } else { if (!FileSystem::setModTime(filePath, item->_modtime)) { item->_instruction = CSYNC_INSTRUCTION_ERROR; item->_errorString = tr("Could not update file metadata: %1").arg(filePath); - emit itemCompleted(item); + emit itemCompleted(item, ErrorCategory::GenericError); return; } } @@ -412,7 +412,7 @@ void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item) } // This might have changed the shared flag, so we must notify SyncFileStatusTracker for example - emit itemCompleted(item); + emit itemCompleted(item, ErrorCategory::NoError); } else { // Update only outdated data from the disk. @@ -516,7 +516,7 @@ void SyncEngine::startSync() if (!QDir(_localPath).exists()) { _anotherSyncNeeded = DelayedFollowUp; // No _tr, it should only occur in non-mirall - Q_EMIT syncError(QStringLiteral("Unable to find local sync folder.")); + Q_EMIT syncError(QStringLiteral("Unable to find local sync folder."), ErrorCategory::GenericError); finalize(false); return; } @@ -533,7 +533,7 @@ void SyncEngine::startSync() "Placeholders are postfixed with file sizes using Utility::octetsToString()") .arg( Utility::octetsToString(freeBytes), - Utility::octetsToString(minFree))); + Utility::octetsToString(minFree)), ErrorCategory::GenericError); finalize(false); return; } else { @@ -562,7 +562,7 @@ void SyncEngine::startSync() // This creates the DB if it does not exist yet. if (!_journal->open()) { qCWarning(lcEngine) << "No way to create a sync journal!"; - Q_EMIT syncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder.")); + Q_EMIT syncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder."), ErrorCategory::GenericError); finalize(false); return; // database creation error! @@ -578,7 +578,7 @@ void SyncEngine::startSync() _lastLocalDiscoveryStyle = _localDiscoveryStyle; if (_syncOptions._vfs->mode() == Vfs::WithSuffix && _syncOptions._vfs->fileSuffix().isEmpty()) { - Q_EMIT syncError(tr("Using virtual files with suffix, but suffix is not set")); + Q_EMIT syncError(tr("Using virtual files with suffix, but suffix is not set"), ErrorCategory::GenericError); finalize(false); return; } @@ -590,7 +590,7 @@ void SyncEngine::startSync() qCInfo(lcEngine) << (usingSelectiveSync ? "Using Selective Sync" : "NOT Using Selective Sync"); } else { qCWarning(lcEngine) << "Could not retrieve selective sync list from DB"; - Q_EMIT syncError(tr("Unable to read the blacklist from the local database")); + Q_EMIT syncError(tr("Unable to read the blacklist from the local database"), ErrorCategory::GenericError); finalize(false); return; } @@ -631,7 +631,7 @@ void SyncEngine::startSync() _discoveryPhase->setSelectiveSyncWhiteList(_journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, &ok)); if (!ok) { qCWarning(lcEngine) << "Unable to read selective sync list, aborting."; - Q_EMIT syncError(tr("Unable to read from the sync journal.")); + Q_EMIT syncError(tr("Unable to read from the sync journal."), ErrorCategory::GenericError); finalize(false); return; } @@ -654,8 +654,8 @@ void SyncEngine::startSync() connect(_discoveryPhase.data(), &DiscoveryPhase::itemDiscovered, this, &SyncEngine::slotItemDiscovered); connect(_discoveryPhase.data(), &DiscoveryPhase::newBigFolder, this, &SyncEngine::newBigFolder); - connect(_discoveryPhase.data(), &DiscoveryPhase::fatalError, this, [this](const QString &errorString) { - Q_EMIT syncError(errorString); + connect(_discoveryPhase.data(), &DiscoveryPhase::fatalError, this, [this](const QString &errorString, ErrorCategory errorCategory) { + Q_EMIT syncError(errorString, errorCategory); finalize(false); }); connect(_discoveryPhase.data(), &DiscoveryPhase::finished, this, &SyncEngine::slotDiscoveryFinished); @@ -760,7 +760,7 @@ void SyncEngine::slotDiscoveryFinished() // Sanity check if (!_journal->open()) { qCWarning(lcEngine) << "Bailing out, DB failure"; - Q_EMIT syncError(tr("Cannot open the sync journal")); + Q_EMIT syncError(tr("Cannot open the sync journal"), ErrorCategory::GenericError); finalize(false); return; } else { @@ -892,9 +892,9 @@ void SyncEngine::slotDiscoveryFinished() finish(); } -void SyncEngine::slotCleanPollsJobAborted(const QString &error) +void SyncEngine::slotCleanPollsJobAborted(const QString &error, const ErrorCategory errorCategory) { - syncError(error); + syncError(error, errorCategory); finalize(false); } @@ -914,12 +914,12 @@ void SyncEngine::setNetworkLimits(int upload, int download) } } -void SyncEngine::slotItemCompleted(const SyncFileItemPtr &item) +void SyncEngine::slotItemCompleted(const SyncFileItemPtr &item, const ErrorCategory category) { _progressInfo->setProgressComplete(*item); emit transmissionProgress(*_progressInfo); - emit itemCompleted(item); + emit itemCompleted(item, category); } void SyncEngine::slotPropagationFinished(bool success) @@ -1211,7 +1211,7 @@ void SyncEngine::slotSummaryError(const QString &message) return; _uniqueErrors.insert(message); - emit syncError(message, ErrorCategory::Normal); + emit syncError(message, ErrorCategory::GenericError); } void SyncEngine::slotInsufficientLocalStorage() diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 3db1b3f2a..ba81fe237 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -163,16 +163,16 @@ signals: void aboutToPropagate(OCC::SyncFileItemVector &); // after each item completed by a job (successful or not) - void itemCompleted(const OCC::SyncFileItemPtr &); + void itemCompleted(const OCC::SyncFileItemPtr &item, const OCC::ErrorCategory category); void transmissionProgress(const OCC::ProgressInfo &progress); void itemDiscovered(const OCC::SyncFileItemPtr &); /// We've produced a new sync error of a type. - void syncError(const QString &message, OCC::ErrorCategory category = OCC::ErrorCategory::Normal); + void syncError(const QString &message, const OCC::ErrorCategory category); - void addErrorToGui(OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject); + void addErrorToGui(const OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const OCC::ErrorCategory category); void finished(bool success); void started(); @@ -208,11 +208,11 @@ private slots: */ void slotNewItem(const OCC::SyncFileItemPtr &item); - void slotItemCompleted(const OCC::SyncFileItemPtr &item); + void slotItemCompleted(const OCC::SyncFileItemPtr &item, const OCC::ErrorCategory category); void slotDiscoveryFinished(); void slotPropagationFinished(bool success); void slotProgress(const OCC::SyncFileItem &item, qint64 curent); - void slotCleanPollsJobAborted(const QString &error); + void slotCleanPollsJobAborted(const QString &error, const OCC::ErrorCategory category); /** Records that a file was touched by a job. */ void slotAddTouchedFile(const QString &fn); diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.cpp b/src/libsync/vfs/cfapi/cfapiwrapper.cpp index 059844395..8045c6148 100644 --- a/src/libsync/vfs/cfapi/cfapiwrapper.cpp +++ b/src/libsync/vfs/cfapi/cfapiwrapper.cpp @@ -355,7 +355,7 @@ OCC::CfApiWrapper::PlaceHolderInfo::PlaceHolderInfo(CF_PLACEHOLDER_BASIC_INFO *d { } -OCC::Optional OCC::CfApiWrapper::PlaceHolderInfo::pinState() const +OCC::Optional OCC::CfApiWrapper::PlaceHolderInfo::pinState() const { Q_ASSERT(_data); if (!_data) { @@ -682,7 +682,7 @@ OCC::CfApiWrapper::PlaceHolderInfo OCC::CfApiWrapper::findPlaceholderInfo(const } } -OCC::Result OCC::CfApiWrapper::setPinState(const QString &path, OCC::PinStateEnums::PinState state, SetPinRecurseMode mode) +OCC::Result OCC::CfApiWrapper::setPinState(const QString &path, OCC::PinState state, SetPinRecurseMode mode) { const auto cfState = pinStateToCfPinState(state); const auto flags = pinRecurseModeToCfSetPinFlags(mode); diff --git a/test/testactivitylistmodel.cpp b/test/testactivitylistmodel.cpp index 2c537ac0f..c050a091d 100644 --- a/test/testactivitylistmodel.cpp +++ b/test/testactivitylistmodel.cpp @@ -456,6 +456,17 @@ public: QVERIFY(index.isValid()); } + void testActivityAdd(void(OCC::ActivityListModel::*addingMethod)(const OCC::Activity&, OCC::ActivityListModel::ErrorType), OCC::Activity &activity, OCC::ActivityListModel::ErrorType type) { + const auto model = testingALM(); + QCOMPARE(model->rowCount(), 0); + + (model.data()->*addingMethod)(activity, type); + QCOMPARE(model->rowCount(), 1); + + const auto index = model->index(0, 0); + QVERIFY(index.isValid()); + } + private slots: void initTestCase() { @@ -555,7 +566,7 @@ private slots: }; void testAddError() { - testActivityAdd(&TestingALM::addErrorToActivityList, testSyncResultErrorActivity); + testActivityAdd(&TestingALM::addErrorToActivityList, testSyncResultErrorActivity, OCC::ActivityListModel::ErrorType::SyncError); }; void testAddIgnoredFile() { @@ -615,7 +626,7 @@ private slots: model->addSyncFileItemToActivityList(testSyncFileItemActivity); QCOMPARE(model->rowCount(), 51); - model->addErrorToActivityList(testSyncResultErrorActivity); + model->addErrorToActivityList(testSyncResultErrorActivity, OCC::ActivityListModel::ErrorType::SyncError); QCOMPARE(model->rowCount(), 52); model->addIgnoredFileToList(testFileIgnoredActivity);