Skip to content
Open
30 changes: 30 additions & 0 deletions src/common/syncjournaldb.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

#include <QCryptographicHash>

Check failure on line 7 in src/common/syncjournaldb.cpp

View workflow job for this annotation

GitHub Actions / build

src/common/syncjournaldb.cpp:7:10 [clang-diagnostic-error]

'QCryptographicHash' file not found
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
Expand Down Expand Up @@ -219,7 +219,7 @@

// Since the list is sorted, we can do a binary search.
// If the path is a prefix of another item or right after in the lexical order.
auto it = std::lower_bound(list.cbegin(), list.cend(), pathSlash);

Check warning on line 222 in src/common/syncjournaldb.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace with the version of "std::ranges::lower_bound" that takes a range.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiFFV3FK4CNcEhCm&open=AZ4mDiFFV3FK4CNcEhCm&pullRequest=10001

if (it != list.cend() && *it == pathSlash) {
return true;
Expand Down Expand Up @@ -1061,7 +1061,7 @@
return tr("Failed to connect database."); // checkConnect failed.
}

int plen = record._path.length();

Check warning on line 1064 in src/common/syncjournaldb.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

implicit conversion loses integer precision: 'qsizetype' (aka 'long long') to 'int'

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiFFV3FK4CNcEhCk&open=AZ4mDiFFV3FK4CNcEhCk&pullRequest=10001

QByteArray etag(record._etag);
if (etag.isEmpty()) {
Expand Down Expand Up @@ -1169,7 +1169,7 @@
return true;
}

bool SyncJournalDb::listAllE2eeFoldersWithEncryptionStatusLessThan(const int status, const std::function<void(const SyncJournalFileRecord &)> &rowCallback)

Check warning on line 1172 in src/common/syncjournaldb.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiFFV3FK4CNcEhCt&open=AZ4mDiFFV3FK4CNcEhCt&pullRequest=10001
{
QMutexLocker locker(&_mutex);

Expand Down Expand Up @@ -1487,7 +1487,7 @@

bool SyncJournalDb::getFileRecordsByFileId(const QByteArray &fileId, const std::function<void(const SyncJournalFileRecord &)> &rowCallback)
{
QMutexLocker locker(&_mutex);

Check warning on line 1490 in src/common/syncjournaldb.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "locker" of type "class QMutexLocker<class QRecursiveMutex>" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiFFV3FK4CNcEhCy&open=AZ4mDiFFV3FK4CNcEhCy&pullRequest=10001

if (fileId.isEmpty() || _metadataTableIsEmpty) {
return true; // no error, yet nothing found (rec->isValid() == false)
Expand Down Expand Up @@ -2171,6 +2171,36 @@
return entry;
}

bool SyncJournalDb::renameErrorBlacklistPaths(const QString &from, const QString &to)
{
QMutexLocker locker(&_mutex);
if (!checkConnect()) {
return false;
}

SqlQuery countQuery(_db);
countQuery.prepare("SELECT COUNT(*) FROM blacklist "
"WHERE errorCategory = ?1 "
"AND (path = ?2 OR (path > (?2 || '/') AND path < (?2 || '0')))");
Comment on lines +2182 to +2184
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use prepared SQL statements in this method (similar to how it's done in e.g. getFileRecordsByFileId)

countQuery.bindValue(1, SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage);
countQuery.bindValue(2, from);
const bool hasQuotaEntries = countQuery.exec() && countQuery.next().hasData && countQuery.intValue(0) > 0;

// Update the exact folder entry and all entries whose path starts with "from/".
// Uses the same range trick as IS_PREFIX_PATH_OR_EQUAL: '/' + 1 == '0'.
SqlQuery query(_db);
query.prepare("UPDATE blacklist "
"SET path = ?2 || substr(path, length(?1) + 1) "
"WHERE path == ?1 OR (path > (?1 || '/') AND path < (?1 || '0'))");
query.bindValue(1, from);
query.bindValue(2, to);
if (!query.exec()) {
sqlFail(QStringLiteral("renameErrorBlacklistPaths"), query);
}
Comment on lines +2189 to +2199
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be run if there aren't any quota entries?


return hasQuotaEntries;
}

bool SyncJournalDb::deleteStaleErrorBlacklistEntries(const QSet<QString> &keep)
{
QMutexLocker locker(&_mutex);
Expand Down
1 change: 1 addition & 0 deletions src/common/syncjournaldb.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#ifndef SYNCJOURNALDB_H
#define SYNCJOURNALDB_H

#include <QObject>

Check failure on line 10 in src/common/syncjournaldb.h

View workflow job for this annotation

GitHub Actions / build

src/common/syncjournaldb.h:10:10 [clang-diagnostic-error]

'QObject' file not found
#include <QDateTime>
#include <QHash>
#include <QMutex>
Expand Down Expand Up @@ -145,6 +145,7 @@

SyncJournalErrorBlacklistRecord errorBlacklistEntry(const QString &);
[[nodiscard]] bool deleteStaleErrorBlacklistEntries(const QSet<QString> &keep);
bool renameErrorBlacklistPaths(const QString &from, const QString &to);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool renameErrorBlacklistPaths(const QString &from, const QString &to);
[[nodiscard]] bool renameErrorBlacklistPaths(const QString &from, const QString &to);


/// Delete flags table entries that have no metadata correspondent
void deleteStaleFlagsEntries();
Expand Down
82 changes: 74 additions & 8 deletions src/libsync/discovery.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2018 ownCloud GmbH
Expand Down Expand Up @@ -29,7 +29,7 @@
namespace
{
constexpr const char *editorNamesForDelayedUpload[] = {"PowerPDF"};
constexpr const char *fileExtensionsToCheckIfOpenForSigning[] = {".pdf"};

Check warning on line 32 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use "std::array" or "std::vector" instead of a C-style array.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMA&open=AZ4EVJ8Lv7_SaclY-yMA&pullRequest=10001
constexpr auto delayIntervalForSyncRetryForOpenedForSigningFilesSeconds = 60;
constexpr auto delayIntervalForSyncRetryForFilesExceedQuotaSeconds = 60;
}
Expand Down Expand Up @@ -61,7 +61,7 @@
computePinState(parent->_pinState);
}

ProcessDirectoryJob::ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, const PathTuple &path, const SyncFileItemPtr &dirItem, const SyncFileItemPtr &parentDirItem, QueryMode queryLocal, qint64 lastSyncTimestamp, QObject *parent)

Check warning on line 64 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This function has 8 parameters, which is greater than the 7 authorized.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yME&open=AZ4EVJ8Lv7_SaclY-yME&pullRequest=10001
: QObject(parent)
, _dirItem(dirItem)
, _dirParentItem(parentDirItem)
Expand Down Expand Up @@ -252,7 +252,7 @@
QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs);
}

bool ProcessDirectoryJob::handleExcluded(const QString &path, const Entries &entries, const std::map<QString, Entries> &allEntries, const bool isHidden, const bool isBlacklisted)

Check failure on line 255 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 108 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMH&open=AZ4EVJ8Lv7_SaclY-yMH&pullRequest=10001
{
const auto isDirectory = entries.localEntry.isDirectory || entries.serverEntry.isDirectory;

Expand All @@ -276,7 +276,7 @@
|| excluded == CSYNC_FILE_EXCLUDE_TRAILING_SPACE
|| excluded == CSYNC_FILE_EXCLUDE_LEADING_AND_TRAILING_SPACE;

const auto leadingAndTrailingSpacesFilesAllowed = !_discoveryData->_shouldEnforceWindowsFileNameCompatibility || _discoveryData->_leadingAndTrailingSpacesFilesAllowed.contains(_discoveryData->_localDir + path);

Check warning on line 279 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMJ&open=AZ4EVJ8Lv7_SaclY-yMJ&pullRequest=10001
#if defined Q_OS_WINDOWS
if (hasLeadingOrTrailingSpaces && leadingAndTrailingSpacesFilesAllowed) {
#else
Expand Down Expand Up @@ -403,7 +403,7 @@
} else {
char invalid = '\0';
constexpr QByteArrayView invalidChars("\\:?*\"<>|");
for (const auto x : invalidChars) {

Check failure on line 406 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMI&open=AZ4EVJ8Lv7_SaclY-yMI&pullRequest=10001
if (item->_file.contains(x)) {
invalid = x;
break;
Expand All @@ -425,7 +425,7 @@
}
item->_status = SyncFileItem::FileNameInvalid;
break;
case CSYNC_FILE_EXCLUDE_TRAILING_SPACE:

Check warning on line 428 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Reduce this switch case number of lines from 6 to at most 5, for example by extracting code into methods.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMO&open=AZ4EVJ8Lv7_SaclY-yMO&pullRequest=10001
item->_errorString = tr("Filename contains trailing spaces.");
item->_status = SyncFileItem::FileNameInvalid;
if (isLocal && !maybeRenameForWindowsCompatibility(_discoveryData->_localDir + item->_file, excluded)) {
Expand Down Expand Up @@ -1059,9 +1059,14 @@
} else {
// we need to make a request to the server to know that the original file is deleted on the server
_pendingAsyncJobs++;
// Mark this path as a pending rename check so that children blocked from upload
// because of quota errors inside a queued deleted-directory job do not cancel the
// parent's REMOVE instruction before rename detection can claim it.
_discoveryData->_pendingRenameSourcePaths.insert(originalPath);
const auto job = new RequestEtagJob(_discoveryData->_account, _discoveryData->_remoteFolder + originalPath, this);
connect(job, &RequestEtagJob::finishedWithResult, this, [=, this](const HttpResult<QByteArray> &etag) mutable {

Check failure on line 1067 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Explicitly capture the required scope variables.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMU&open=AZ4EVJ8Lv7_SaclY-yMU&pullRequest=10001
_pendingAsyncJobs--;
_discoveryData->_pendingRenameSourcePaths.remove(originalPath);
QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs);
if (etag || etag.error().code != 404 ||
// Somehow another item claimed this original path, consider as if it existed
Expand Down Expand Up @@ -1231,6 +1236,11 @@
}

item->_status = SyncFileItem::Status::NormalError;
// Mark as a quota error so blacklistUpdate writes an InsufficientRemoteStorage entry.
// Without this, _httpErrorCode would be 0 and blacklistUpdate would not create an
// entry, leaving the file unprotected by checkNewDeleteConflict if the remote parent
// folder is deleted before the quota situation is resolved.
item->_httpErrorCode = 507;
Comment on lines +1239 to +1243
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like a hack to me, we probably shouldn't fake a returned HTTP error code

looking at blacklistUpdate there is an unused bool in SyncFileItem _errorMayBeBlacklisted which could be renamed and reused to indicate that this item is excluded due to quota reasons, and then update SyncJournalErrorBlacklistRecord::createBlacklistEntry to also consider that flag for the blacklist entry

_discoveryData->_anotherSyncNeeded = true;
_discoveryData->_filesNeedingScheduledSync.insert(path._original, delayIntervalForSyncRetryForFilesExceedQuotaSeconds);
}
Expand Down Expand Up @@ -1467,7 +1477,7 @@
return;
}

if (checkNewDeleteConflict(item)) {
if (checkNewDeleteConflict(item, localEntry.size)) {
return;
}

Expand Down Expand Up @@ -1570,7 +1580,7 @@
const auto isE2eeMove = isMove && (base.isE2eEncrypted() || isInsideEncryptedTree());
const auto isCfApiVfsMode = _discoveryData->_syncOptions._vfs && _discoveryData->_syncOptions._vfs->mode() == Vfs::WindowsCfApi;
const bool isOnlineOnlyItem = isCfApiVfsMode && (localEntry.isDirectory || _discoveryData->_syncOptions._vfs->isDehydratedPlaceholder(_discoveryData->_localDir + path._local));
const auto isE2eeMoveOnlineOnlyItemWithCfApi = isE2eeMove && isOnlineOnlyItem;

Check warning on line 1583 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMc&open=AZ4EVJ8Lv7_SaclY-yMc&pullRequest=10001

if (isE2eeMove) {
qCDebug(lcDisco) << "requesting permanent deletion for" << originalPath;
Expand Down Expand Up @@ -1650,7 +1660,7 @@
return;
}

auto wasDeletedOnClient = _discoveryData->findAndCancelDeletedJob(originalPath);

Check warning on line 1663 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this declaration by a structured binding declaration.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMe&open=AZ4EVJ8Lv7_SaclY-yMe&pullRequest=10001

auto processRename = [item, originalPath, base, this](PathTuple &path) {
auto adjustedOriginalPath = _discoveryData->adjustRenamedPath(originalPath, SyncFileItem::Down);
Expand Down Expand Up @@ -1694,7 +1704,7 @@
if (base.isVirtualFile() && isVfsWithSuffix())
chopVirtualFileSuffix(serverOriginalPath);
auto job = new RequestEtagJob(_discoveryData->_account, serverOriginalPath, this);
connect(job, &RequestEtagJob::finishedWithResult, this, [=, this](const HttpResult<QByteArray> &etag) mutable {

Check failure on line 1707 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Explicitly capture the required scope variables.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMf&open=AZ4EVJ8Lv7_SaclY-yMf&pullRequest=10001


if (!etag || (etag.get() != base._etag && !item->isDirectory()) || _discoveryData->isRenamed(originalPath)
Expand Down Expand Up @@ -1740,7 +1750,7 @@
// If there's no content hash, use heuristics
if (serverEntry.checksumHeader.isEmpty()) {
// If the size or mtime is different, it's definitely a conflict.
bool isConflict = (serverEntry.size != localEntry.size) || (serverEntry.modtime != localEntry.modtime) || (dbEntry.isValid() && dbEntry._modtime != localEntry.modtime && serverEntry.modtime == localEntry.modtime);

Check warning on line 1753 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "isConflict" of type "_Bool" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMh&open=AZ4EVJ8Lv7_SaclY-yMh&pullRequest=10001

// It could be a conflict even if size and mtime match!
//
Expand Down Expand Up @@ -1896,7 +1906,7 @@
item->_direction = _dirItem->_direction;
}

{

Check warning on line 1909 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested code block into a separate function.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMi&open=AZ4EVJ8Lv7_SaclY-yMi&pullRequest=10001
const auto isImportantInstruction = item->_instruction != CSYNC_INSTRUCTION_NONE;
if (isImportantInstruction) {
qCInfo(lcDisco).noquote() << item->_discoveryResult;
Expand Down Expand Up @@ -2117,7 +2127,7 @@
}

const auto isMatchingFileExtension = std::find_if(std::cbegin(fileExtensionsToCheckIfOpenForSigning), std::cend(fileExtensionsToCheckIfOpenForSigning),
[path](const auto &matchingExtension) {

Check warning on line 2130 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Capture variables by reference, it is safe in this context.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMm&open=AZ4EVJ8Lv7_SaclY-yMm&pullRequest=10001
return path._local.endsWith(matchingExtension, Qt::CaseInsensitive);
}) != std::cend(fileExtensionsToCheckIfOpenForSigning);

Expand All @@ -2130,7 +2140,7 @@

for (const auto &detectedEditorName : editorsKeepingFileBusy) {
const auto isMatchingEditorFound = std::find_if(std::cbegin(editorNamesForDelayedUpload), std::cend(editorNamesForDelayedUpload),
[detectedEditorName](const auto &matchingEditorName) {

Check warning on line 2143 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Capture variables by reference, it is safe in this context.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMo&open=AZ4EVJ8Lv7_SaclY-yMo&pullRequest=10001
return detectedEditorName.processName.startsWith(matchingEditorName, Qt::CaseInsensitive);
}) != std::cend(editorNamesForDelayedUpload);
if (isMatchingEditorFound) {
Expand Down Expand Up @@ -2213,6 +2223,13 @@
// Do not remove a directory that has ignored files
qCInfo(lcDisco) << "Child ignored for a folder to remove" << _dirItem->_file << "direction" << _dirItem->_direction;
_dirItem->_instruction = CSYNC_INSTRUCTION_NONE;
// Invalidate the parent directory's etag so the next sync queries it from
// the server instead of using a ParentNotChanged cache hit. Without this, a
// parent whose etag has not changed since the server side deletion uses its DB
// record as a proxy for server state and keeps issuing NONE for this folder.
const auto slashPos = _dirItem->_file.lastIndexOf(QLatin1Char('/'));
const auto parentPath = slashPos >= 0 ? _dirItem->_file.left(slashPos) : QString();
_discoveryData->_statedb->schedulePathForRemoteDiscovery(parentPath.toUtf8());
}
}
emit finished();
Expand Down Expand Up @@ -2483,7 +2500,7 @@
case CSYNC_FILE_EXCLUDE_LEADING_SPACE:
case CSYNC_FILE_EXCLUDE_TRAILING_SPACE:
{
const auto removeTrailingSpaces = [] (QString string) -> QString {

Check warning on line 2503 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the redundant return type of this lambda.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVJ8Lv7_SaclY-yMy&open=AZ4EVJ8Lv7_SaclY-yMy&pullRequest=10001
for (int n = string.size() - 1; n >= 0; -- n) {
if (!string.at(n).isSpace()) {
string.truncate(n + 1);
Expand All @@ -2504,18 +2521,67 @@
return result;
}

bool ProcessDirectoryJob::checkNewDeleteConflict(const SyncFileItemPtr &item) const
bool ProcessDirectoryJob::checkNewDeleteConflict(const SyncFileItemPtr &item, int64_t localFileSize)

Check warning on line 2524 in src/libsync/discovery.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "localFileSize" of type "long" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4oYHA_hUm3UEh_qoEu&open=AZ4oYHA_hUm3UEh_qoEu&pullRequest=10001
{
if (_discoveryData->recursiveCheckForDeletedParents(item->_file)) {
qCWarning(lcDisco) << "Removing local file inside a remotely deleted folder" << item->_file;
item->_instruction = CSYNC_INSTRUCTION_REMOVE;
item->_direction = SyncFileItem::Down;
item->_wantsSpecificActions = SyncFileItem::SynchronizationOptions::MoveToClientTrashBin;
if (!_discoveryData->recursiveCheckForDeletedParents(item->_file)) {
return false;
}

// Deleting the local copy could result in permanent data loss if the file was never in the
// server and blocked from being uploaded by a quota error.
// Protect it instead and let the user resolve the storage situation first.
if (const auto blacklistEntry = _discoveryData->_statedb->errorBlacklistEntry(item->_file);
blacklistEntry.isValid()
&& blacklistEntry._errorCategory == SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage) {
qCWarning(lcDisco) << "Not removing local file inside a remotely deleted folder: "
"file was never uploaded due to storage quota —"
<< item->_file;
item->_instruction = CSYNC_INSTRUCTION_ERROR;
item->_status = SyncFileItem::SoftError;
// Keep _httpErrorCode at 507 so blacklistUpdate recreates the InsufficientRemoteStorage
// entry if the ignore duration later expires.
item->_httpErrorCode = 507;
item->_errorString = tr("\"%1\" was not deleted because its latest changes were not synced "
"and your server quota was exceeded. "
"Please manage your storage and try syncing again.")
.arg(item->_file);
// Prevent the parent directory from being deleted while a file with an error exists.
_childIgnored = true;
emit _discoveryData->itemDiscovered(item);
return true;
}

return false;
// No prior blacklist entry. For files blocked from upload due to a quota error in the same
// sync as the folder deletion, check the parent folder's last known quota from the DB.
if (!item->isDirectory() && localFileSize > 0 && _dirItem) {
SyncJournalFileRecord dirItemDbRecord;
if (_discoveryData->_statedb->getFileRecord(_dirItem->_file, &dirItemDbRecord)
&& dirItemDbRecord.isValid()) {
const auto bytesAvailable = dirItemDbRecord._folderQuota.bytesAvailable;
if (bytesAvailable >= 0 && localFileSize > bytesAvailable) {
qCWarning(lcDisco) << "Not removing local file inside a remotely deleted folder: "
"file would exceed last known parent folder quota —"
<< item->_file;
item->_instruction = CSYNC_INSTRUCTION_ERROR;
item->_status = SyncFileItem::SoftError;
item->_httpErrorCode = 507;
item->_errorString = tr("\"%1\" was not deleted because its latest changes were not synced "
"and your server quota was exceeded. "
"Please manage your storage and try syncing again.")
.arg(item->_file);
_childIgnored = true;
emit _discoveryData->itemDiscovered(item);
return true;
}
}
}

qCWarning(lcDisco) << "Removing local file inside a remotely deleted folder" << item->_file;
item->_instruction = CSYNC_INSTRUCTION_REMOVE;
item->_direction = SyncFileItem::Down;
item->_wantsSpecificActions = SyncFileItem::SynchronizationOptions::MoveToClientTrashBin;
emit _discoveryData->itemDiscovered(item);
return true;
}

}
2 changes: 1 addition & 1 deletion src/libsync/discovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#pragma once

#include <QObject>

Check failure on line 9 in src/libsync/discovery.h

View workflow job for this annotation

GitHub Actions / build

src/libsync/discovery.h:9:10 [clang-diagnostic-error]

'QObject' file not found
#include <cstdint>
#include "csync_exclude.h"
#include "discoveryphase.h"
Expand Down Expand Up @@ -249,10 +249,10 @@
*/
void setupDbPinStateActions(SyncJournalFileRecord &record);

bool maybeRenameForWindowsCompatibility(const QString &absoluteFileName,

Check warning on line 252 in src/libsync/discovery.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4EVKAPv7_SaclY-yM0&open=AZ4EVKAPv7_SaclY-yM0&pullRequest=10001
CSYNC_EXCLUDE_TYPE excludeReason);

[[nodiscard]] bool checkNewDeleteConflict(const SyncFileItemPtr &item) const;
[[nodiscard]] bool checkNewDeleteConflict(const SyncFileItemPtr &item, int64_t localFileSize = 0);

qint64 _lastSyncTimestamp = 0;

Expand Down
6 changes: 6 additions & 0 deletions src/libsync/discoveryphase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@

// it is not too big, put it in the white list (so we will not do more query for the children) and and do not block.
const auto sanitisedPath = Utility::trailingSlashPath(path);
_selectiveSyncWhiteList.insert(std::upper_bound(_selectiveSyncWhiteList.begin(), _selectiveSyncWhiteList.end(), sanitisedPath), sanitisedPath);

Check warning on line 123 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace with the version of "std::ranges::upper_bound" that takes a range.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkF&open=AZ4np06GZxHbP9IiDVkF&pullRequest=10001
return callback(false);
});
}
Expand Down Expand Up @@ -237,6 +237,12 @@
continue;
}

// Async 404 in flight: rename source candidate, not a confirmed deletion.
if (_pendingRenameSourcePaths.contains(currentParentFolder)) {
qCDebug(lcDiscovery()) << "deleted parent is a pending rename candidate, skipping" << currentParentFolder;
continue;
}

qCDebug(lcDiscovery()) << "deleted parent found";
result = true;
break;
Expand Down Expand Up @@ -441,7 +447,7 @@
void DiscoverySingleDirectoryJob::start()
{
// Start the actual HTTP job
auto *lsColJob = new LsColJob(_account, _subPath);

Check warning on line 450 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "lsColJob" of type "class OCC::LsColJob *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkN&open=AZ4np06GZxHbP9IiDVkN&pullRequest=10001

Check failure on line 450 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the use of "new" with an operation that automatically manages the memory.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkO&open=AZ4np06GZxHbP9IiDVkO&pullRequest=10001

const auto props = LsColJob::defaultProperties(_isRootPath ? LsColJob::FolderType::RootFolder : LsColJob::FolderType::ChildFolder,
_account);
Expand Down Expand Up @@ -544,7 +550,7 @@
}
} else {
RemoteInfo result;
int slash = file.lastIndexOf(u'/');

Check warning on line 553 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

implicit conversion loses integer precision: 'qsizetype' (aka 'long long') to 'int'

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkC&open=AZ4np06GZxHbP9IiDVkC&pullRequest=10001

Check warning on line 553 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "slash" of type "int" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkQ&open=AZ4np06GZxHbP9IiDVkQ&pullRequest=10001
result.name = file.mid(slash + 1);
result.size = -1;
LsColJob::propertyMapToRemoteInfo(map,
Expand Down Expand Up @@ -654,7 +660,7 @@

const auto jsonMetadata = statusCode == 404 ? QByteArray{} : json.toJson(QJsonDocument::Compact);
const auto jsonMetadataVersion = FolderMetadata::setupVersionFromExistingMetadata(jsonMetadata);
switch (jsonMetadataVersion) {

Check failure on line 663 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a "default" case to this switch statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkW&open=AZ4np06GZxHbP9IiDVkW&pullRequest=10001
case FolderMetadata::MetadataVersion::VersionUndefined:
case FolderMetadata::MetadataVersion::Version1:
case FolderMetadata::MetadataVersion::Version1_2:
Expand All @@ -671,12 +677,12 @@
break;
}

const auto e2EeFolderMetadata = new FolderMetadata(_account,

Check failure on line 680 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the use of "new" with an operation that automatically manages the memory.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkX&open=AZ4np06GZxHbP9IiDVkX&pullRequest=10001
_remoteRootFolderPath,
jsonMetadata,
RootEncryptedFolderInfo(Utility::fullRemotePathToRemoteSyncRootRelative(topLevelFolderPath, _remoteRootFolderPath)),
job->signature());
connect(e2EeFolderMetadata, &FolderMetadata::setupComplete, this, [this, e2EeFolderMetadata] {

Check warning on line 685 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This lambda has 37 lines, which is greater than the 20 lines authorized. Split it into several lambdas or functions, or make it a named function.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkY&open=AZ4np06GZxHbP9IiDVkY&pullRequest=10001
e2EeFolderMetadata->deleteLater();
if (!e2EeFolderMetadata->isValid()) {
emit finished(HttpError{0, tr("Encrypted metadata setup error!")});
Expand Down Expand Up @@ -718,7 +724,7 @@
emit finished(_results);
deleteLater();
});
}

Check failure on line 727 in src/libsync/discoveryphase.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Potential leak of memory pointed to by 'e2EeFolderMetadata'

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4np06GZxHbP9IiDVkb&open=AZ4np06GZxHbP9IiDVkb&pullRequest=10001

void DiscoverySingleDirectoryJob::metadataError(const QByteArray &fileId, int httpReturnCode)
{
Expand Down
8 changes: 8 additions & 0 deletions src/libsync/discoveryphase.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ class DiscoveryPhase : public QObject
/// contains files/folder names that are requested to be deleted permanently
QSet<QString> _permanentDeletionRequests;

/** Paths with an async 404 in flight to confirm server deletion.
*
* While a path is here, children blocked from upload because of quota errors must not
* prevent the parent folder from being removed. Rename detection takes priority and
* will claim the path once the 404 confirms deletion.
*/
QSet<QString> _pendingRenameSourcePaths;

void markPermanentDeletionRequests();

public:
Expand Down
19 changes: 19 additions & 0 deletions src/libsync/owncloudpropagator.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2013 ownCloud GmbH
Expand Down Expand Up @@ -160,6 +160,9 @@

if (item._httpErrorCode == 507) {
entry._errorCategory = SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage;
// Quota can change at any time (user frees space, admin adjusts limits).
// Always retry on the next sync rather than backing off exponentially.
entry._ignoreDuration = 0;
}

return entry;
Expand Down Expand Up @@ -390,7 +393,7 @@
case CSYNC_INSTRUCTION_UPDATE_ENCRYPTION_METADATA:
{
const auto rootE2eeFolderPath = item->_file.split('/').first();
const auto rootE2eeFolderPathFullRemotePath = fullRemotePath(rootE2eeFolderPath);

Check warning on line 396 in src/libsync/owncloudpropagator.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhuqV3FK4CNcEhBW&open=AZ4mDhuqV3FK4CNcEhBW&pullRequest=10001
return new UpdateMigratedE2eeMetadataJob(this, item, rootE2eeFolderPathFullRemotePath, remotePath());
}
case CSYNC_INSTRUCTION_IGNORE:
Expand Down Expand Up @@ -696,7 +699,7 @@
(item->_instruction == CSYNC_INSTRUCTION_NEW || item->_instruction == CSYNC_INSTRUCTION_SYNC);
const auto isVirtualFile = item->_type == ItemTypeVirtualFile;
const auto isEncrypted = item->_e2eEncryptionStatus != SyncFileItem::EncryptionStatus::NotEncrypted;
const auto shouldAddBulkPropagateDownloadItem = isDownload && isVirtualFile && isVfsCfApi && !isEncrypted;

Check warning on line 702 in src/libsync/owncloudpropagator.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhuqV3FK4CNcEhBb&open=AZ4mDhuqV3FK4CNcEhBb&pullRequest=10001

if (shouldAddBulkPropagateDownloadItem) {
addBulkPropagateDownloadItem(item, directories);
Expand Down Expand Up @@ -724,7 +727,7 @@
}
);

if (foundBulkPrpagatorDownloadJobIt == std::cend(directories.top().second->_subJobs._jobsToDo)) {

Check warning on line 730 in src/libsync/owncloudpropagator.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the init-statement to declare "foundBulkPrpagatorDownloadJobIt" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhuqV3FK4CNcEhBc&open=AZ4mDhuqV3FK4CNcEhBc&pullRequest=10001
bulkPropagatorDownloadJob = new BulkPropagatorDownloadJob(this, directories.top().second);
directories.top().second->appendJob(bulkPropagatorDownloadJob);
} else {
Expand All @@ -741,7 +744,7 @@
const auto rootE2eeFolderPathWithSlash = QString(rootE2eeFolderPath + "/");

QPair<QString, PropagateDirectory *> foundDirectory = {QString{}, nullptr};
for (auto it = std::rbegin(directories); it != std::rend(directories); ++it) {

Check warning on line 747 in src/libsync/owncloudpropagator.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this loop so that it is less error-prone.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhuqV3FK4CNcEhBh&open=AZ4mDhuqV3FK4CNcEhBh&pullRequest=10001
if (it->first == rootE2eeFolderPathWithSlash) {
foundDirectory = *it;
break;
Expand Down Expand Up @@ -1384,7 +1387,7 @@

void PropagateDirectory::willDeleteItemToClientTrashBin(const SyncFileItemPtr &item)
{
auto deleteFolderJob = dynamic_cast<PropagateLocalRemove*>(_firstJob.get());

Check warning on line 1390 in src/libsync/owncloudpropagator.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "deleteFolderJob" of type "class OCC::PropagateLocalRemove *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhuqV3FK4CNcEhBp&open=AZ4mDhuqV3FK4CNcEhBp&pullRequest=10001
if (!deleteFolderJob) {
return;
}
Expand Down Expand Up @@ -1509,7 +1512,7 @@
if (!_item->_remotePerm.isNull() &&
!_item->_remotePerm.hasPermissionsForReadWrite()) {
try {
if (const auto fileName = propagator()->fullLocalPath(_item->_file); FileSystem::fileExists(fileName)) {

Check failure on line 1515 in src/libsync/owncloudpropagator.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhuqV3FK4CNcEhBq&open=AZ4mDhuqV3FK4CNcEhBq&pullRequest=10001
FileSystem::setFolderPermissions(fileName, FileSystem::FolderPermissions::ReadOnly);
Q_EMIT propagator()->touchedFile(fileName);
}
Expand Down Expand Up @@ -1586,6 +1589,22 @@
}
}
}

// For a DOWN rename where the directory itself succeeded (PropagateLocalRename
// ran OK) but one or more children had errors, still persist the renamed
// directory's path and fileId in the database. Without this record a
// subsequent server side rename of the same directory cannot be matched via
// fileId lookup, causing the client to treat it as a DELETE + NEW instead of
// following the move.
if (!_item->isEmpty()
&& status != SyncFileItem::Success
&& status != SyncFileItem::FatalError
&& _item->_status == SyncFileItem::Success
&& _item->_instruction == CSYNC_INSTRUCTION_RENAME
&& _item->_direction == SyncFileItem::Down) {
propagator()->updateMetadata(*_item);
}

_state = Finished;
qCDebug(lcDirectory()) << "PropagateDirectory::slotSubJobsFinished" << "emit finished" << status;
emit finished(status);
Expand Down Expand Up @@ -1790,7 +1809,7 @@
return _remoteFolder + tmp_file_name;
}

QString OwncloudPropagator::fulllRemotePathToPathInSyncJournalDb(const QString &fullRemotePath) const

Check warning on line 1812 in src/libsync/owncloudpropagator.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhuqV3FK4CNcEhBy&open=AZ4mDhuqV3FK4CNcEhBy&pullRequest=10001
{
auto result = _remoteFolder != QStringLiteral("/") ? fullRemotePath.mid(_remoteFolder.size()) : fullRemotePath;
if (result.startsWith("/")) {
Expand Down
3 changes: 3 additions & 0 deletions src/libsync/propagateremotemove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,9 @@ void PropagateRemoteMove::finalize()
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"), ErrorCategory::GenericError);
return;
}
if (propagator()->_journal->renameErrorBlacklistPaths(_item->_file, _item->_renameTarget)) {
emit propagator()->insufficientRemoteStorage();
}
}

propagator()->_journal->commit("Remote Rename");
Expand Down
3 changes: 3 additions & 0 deletions src/libsync/propagatorjobs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
},
nullptr,
nullptr,
[this] (const QString &itemPath, QString *removeError) -> bool {

Check warning on line 68 in src/libsync/propagatorjobs.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "removeError" of type "class QString *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiCzV3FK4CNcEhCR&open=AZ4mDiCzV3FK4CNcEhCR&pullRequest=10001
auto result = false;

if (_deleteToClientTrashBin.contains(itemPath)) {
Expand Down Expand Up @@ -138,7 +138,7 @@
{
qCWarning(lcPropagateLocalRemove) << "exception when checking parent folder read only status" << e.what();
}
catch (...)

Check warning on line 141 in src/libsync/propagatorjobs.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

"catch" a specific exception type.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiCzV3FK4CNcEhCV&open=AZ4mDiCzV3FK4CNcEhCV&pullRequest=10001
{
qCWarning(lcPropagateLocalRemove) << "exception when checking parent folder read only status";
}
Expand Down Expand Up @@ -202,7 +202,7 @@
_deleteExistingFile = enabled;
}

void PropagateLocalMkdir::startLocalMkdir()

Check failure on line 205 in src/libsync/propagatorjobs.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 33 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiCzV3FK4CNcEhCW&open=AZ4mDiCzV3FK4CNcEhCW&pullRequest=10001
{
QDir newDir(propagator()->fullLocalPath(_item->_file));
QString newDirStr = QDir::toNativeSeparators(newDir.path());
Expand Down Expand Up @@ -523,7 +523,7 @@
}
} else if (!fileAlreadyMoved) {
qCDebug(lcPropagateLocalRename) << "propagate child items after move from" << existingFile << "to" << targetFile;
const auto dbQueryResult = propagator()->_journal->getFilesBelowPath(previousNameInDb.toUtf8(), [previousNameInDb, this] (const SyncJournalFileRecord &record) -> void {

Check warning on line 526 in src/libsync/propagatorjobs.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This lambda has 25 lines, which is greater than the 20 lines authorized. Split it into several lambdas or functions, or make it a named function.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDiCzV3FK4CNcEhCe&open=AZ4mDiCzV3FK4CNcEhCe&pullRequest=10001
const auto oldFileNameString = propagator()->adjustRenamedPath(QString::fromUtf8(record._path));
auto newFileNameString = oldFileNameString;
newFileNameString.replace(0, previousNameInDb.length(), _item->_renameTarget);
Expand Down Expand Up @@ -561,6 +561,9 @@
done(SyncFileItem::FatalError, tr("Failed to rename file"), ErrorCategory::GenericError);
return;
}
if (propagator()->_journal->renameErrorBlacklistPaths(oldFile, _item->_renameTarget)) {
emit propagator()->insufficientRemoteStorage();
}
}
if (pinState != PinState::Inherited && !vfs->setPinState(_item->_renameTarget, pinState)) {
done(SyncFileItem::NormalError, tr("Error setting pin state"), ErrorCategory::GenericError);
Expand Down
10 changes: 10 additions & 0 deletions src/libsync/syncengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@

item._hasBlacklistEntry = true;

// Discovery already produced a deliberate error for items protected in checkNewDeleteConflict.
// Keep its instruction and message intact so the specific reason is displayed.
// Still show the quota notification if applicable.
if (item._instruction == CSYNC_INSTRUCTION_ERROR) {
if (entry._errorCategory == SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage) {
slotInsufficientRemoteStorage();
}
return true;
}

// If duration has expired, it's not blacklisted anymore
time_t now = Utility::qDateTimeToTime_t(QDateTime::currentDateTimeUtc());
if (now >= entry._lastTryTime + entry._ignoreDuration) {
Expand Down Expand Up @@ -241,7 +251,7 @@

// Delete the stales chunk on the server.
if (account()->capabilities().chunkingNg()) {
for (uint transferId : std::as_const(ids)) {

Check warning on line 254 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "transferId" of type "unsigned int" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhB0&open=AZ4mDhyRV3FK4CNcEhB0&pullRequest=10001
if (!transferId)
continue; // Was not a chunked upload
QUrl url = Utility::concatUrlPath(account()->url(), QLatin1String("remote.php/dav/uploads/") + account()->davUser() + QLatin1Char('/') + QString::number(transferId));
Expand Down Expand Up @@ -397,7 +407,7 @@

// Update on-disk virtual file metadata
if (modificationHappened && item->_type == ItemTypeVirtualFile) {
auto r = _syncOptions._vfs->updateMetadata(*item, filePath, {});

Check warning on line 410 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "r" of type "class OCC::Result<enum OCC::Vfs::ConvertToPlaceholderResult, class QString>" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhB2&open=AZ4mDhyRV3FK4CNcEhB2&pullRequest=10001
if (!r) {
item->_status = SyncFileItem::Status::NormalError;
item->_instruction = CSYNC_INSTRUCTION_ERROR;
Expand Down Expand Up @@ -474,7 +484,7 @@
_needsUpdate = true;

// Insert sorted
auto it = std::lower_bound( _syncItems.begin(), _syncItems.end(), item ); // the _syncItems is sorted

Check warning on line 487 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace with the version of "std::ranges::lower_bound" that takes a range.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhB3&open=AZ4mDhyRV3FK4CNcEhB3&pullRequest=10001
_syncItems.insert( it, item );

slotNewItem(item);
Expand Down Expand Up @@ -502,10 +512,10 @@

if (!e2EeLockedFolders.isEmpty()) {
for (const auto &e2EeLockedFolder : e2EeLockedFolders) {
const auto folderId = e2EeLockedFolder.first;

Check warning on line 515 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid this unnecessary copy by using a "const" reference.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhB7&open=AZ4mDhyRV3FK4CNcEhB7&pullRequest=10001
qCInfo(lcEngine()) << "start unlock job for folderId:" << folderId;
const auto folderToken = EncryptionHelper::decryptStringAsymmetric(_account->e2e()->getCertificateInformation(), _account->e2e()->paddingMode(), *_account->e2e(), e2EeLockedFolder.second);
if (!folderToken) {

Check failure on line 518 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhB5&open=AZ4mDhyRV3FK4CNcEhB5&pullRequest=10001
qCWarning(lcEngine()) << "decrypt failed";
return;
}
Expand Down Expand Up @@ -700,7 +710,7 @@
connect(_discoveryPhase.get(), &DiscoveryPhase::itemDiscovered, this, &SyncEngine::slotItemDiscovered);
connect(_discoveryPhase.get(), &DiscoveryPhase::newBigFolder, this, &SyncEngine::newBigFolder);
connect(_discoveryPhase.get(), &DiscoveryPhase::existingFolderNowBig, this, &SyncEngine::existingFolderNowBig);
connect(_discoveryPhase.get(), &DiscoveryPhase::fatalError, this, [this](const QString &errorString, ErrorCategory errorCategory) {

Check warning on line 713 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "errorCategory" of type "enum OCC::ErrorCategory" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhB8&open=AZ4mDhyRV3FK4CNcEhB8&pullRequest=10001
Q_EMIT syncError(errorString, errorCategory);
finalize(false);
});
Expand Down Expand Up @@ -917,7 +927,7 @@
detectFileLock(item);
}

void SyncEngine::slotPropagationFinished(OCC::SyncFileItem::Status status)

Check warning on line 930 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "status" of type "enum OCC::SyncFileItem::Status" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhB-&open=AZ4mDhyRV3FK4CNcEhB-&pullRequest=10001
{
if (_propagator->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) {
_anotherSyncNeeded = ImmediateFollowUp;
Expand Down Expand Up @@ -1177,7 +1187,7 @@
}
const auto filesDeletedThresholdExceeded = deletionCounter > ConfigFile().deleteFilesThreshold();

if ((allFilesDeleted || filesDeletedThresholdExceeded) && displayDialog) {

Check warning on line 1190 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the init-statement to declare "filesDeletedThresholdExceeded" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhCB&open=AZ4mDhyRV3FK4CNcEhCB&pullRequest=10001
qCWarning(lcEngine) << "Many files are going to be deleted, asking the user";
int side = 0; // > 0 means more deleted on the server. < 0 means more deleted on the client
for (const auto &it : std::as_const(_syncItems)) {
Expand Down Expand Up @@ -1278,7 +1288,7 @@
_leadingAndTrailingSpacesFilesAllowed.append(filePath);
}

void SyncEngine::setLocalDiscoveryEnforceWindowsFileNameCompatibility(bool value)

Check warning on line 1291 in src/libsync/syncengine.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this identifier to be shorter or equal to 31 characters.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ4mDhyRV3FK4CNcEhCJ&open=AZ4mDhyRV3FK4CNcEhCJ&pullRequest=10001
{
_shouldEnforceWindowsFileNameCompatibility = value;
}
Expand Down
Loading
Loading