OCC::FileSystem::setModTime(_rootDir.filePath(relativePath), OCC::Utility::qDateTimeToTime_t(modTime));
}
+void DiskFileModifier::modifyLockState([[maybe_unused]] const QString &relativePath, [[maybe_unused]] LockState lockState, [[maybe_unused]] int lockType, [[maybe_unused]] const QString &lockOwner, [[maybe_unused]] const QString &lockOwnerId, [[maybe_unused]] const QString &lockEditorId, [[maybe_unused]] quint64 lockTime, [[maybe_unused]] quint64 lockTimeout)
+{
+}
+
FileInfo FileInfo::A12_B12_C12_S12()
{
FileInfo fi { QString {}, {
file->lastModified = modTime;
}
+void FileInfo::modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout)
+{
+ FileInfo *file = findInvalidatingEtags(relativePath);
+ Q_ASSERT(file);
+ file->lockState = lockState;
+ file->lockType = lockType;
+ file->lockOwner = lockOwner;
+ file->lockOwnerId = lockOwnerId;
+ file->lockEditorId = lockEditorId;
+ file->lockTime = lockTime;
+ file->lockTimeout = lockTimeout;
+}
+
FileInfo *FileInfo::find(PathComponents pathComponents, const bool invalidateEtags)
{
if (pathComponents.isEmpty()) {
xml.writeTextElement(ocUri, QStringLiteral("permissions"), !fileInfo.permissions.isNull() ? QString(fileInfo.permissions.toString()) : fileInfo.isShared ? QStringLiteral("SRDNVCKW") : QStringLiteral("RDNVCKW"));
xml.writeTextElement(ocUri, QStringLiteral("id"), QString::fromUtf8(fileInfo.fileId));
xml.writeTextElement(ocUri, QStringLiteral("checksums"), QString::fromUtf8(fileInfo.checksums));
+ xml.writeTextElement(ncUri, QStringLiteral("lock-owner"), fileInfo.lockOwnerId);
+ xml.writeTextElement(ncUri, QStringLiteral("lock"), fileInfo.lockState == FileInfo::LockState::FileLocked ? QStringLiteral("1") : QStringLiteral("0"));
+ xml.writeTextElement(ncUri, QStringLiteral("lock-owner-type"), fileInfo.lockOwnerId);
+ xml.writeTextElement(ncUri, QStringLiteral("lock-owner-displayname"), fileInfo.lockOwnerId);
+ xml.writeTextElement(ncUri, QStringLiteral("lock-owner-editor"), fileInfo.lockOwnerId);
+ xml.writeTextElement(ncUri, QStringLiteral("lock-time"), QString::number(fileInfo.lockTime));
+ xml.writeTextElement(ncUri, QStringLiteral("lock-timeout"), QString::number(fileInfo.lockTimeout));
buffer.write(fileInfo.extraDavProperties);
xml.writeEndElement(); // prop
xml.writeTextElement(davUri, QStringLiteral("status"), QStringLiteral("HTTP/1.1 200 OK"));
class FileModifier
{
public:
+ enum class LockState {
+ FileLocked,
+ FileUnlocked,
+ };
+
virtual ~FileModifier() = default;
virtual void remove(const QString &relativePath) = 0;
virtual void insert(const QString &relativePath, qint64 size = 64, char contentChar = 'W') = 0;
virtual void mkdir(const QString &relativePath) = 0;
virtual void rename(const QString &relativePath, const QString &relativeDestinationDirectory) = 0;
virtual void setModTime(const QString &relativePath, const QDateTime &modTime) = 0;
+ virtual void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) = 0;
};
class DiskFileModifier : public FileModifier
void mkdir(const QString &relativePath) override;
void rename(const QString &from, const QString &to) override;
void setModTime(const QString &relativePath, const QDateTime &modTime) override;
+ void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) override;
};
class FileInfo : public FileModifier
void setModTimeKeepEtag(const QString &relativePath, const QDateTime &modTime);
+ void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) override;
+
FileInfo *find(PathComponents pathComponents, const bool invalidateEtags = false);
FileInfo *createDir(const QString &relativePath);
QByteArray extraDavProperties;
qint64 size = 0;
char contentChar = 'W';
+ LockState lockState = LockState::FileUnlocked;
+ int lockType = 0;
+ QString lockOwner;
+ QString lockOwnerId;
+ QString lockEditorId;
+ quint64 lockTime = 0;
+ quint64 lockTimeout = 0;
// Sorted by name to be able to compare trees
QMap<QString, FileInfo> children;
fakeFolder.remoteModifier().insert(fooFileRootFolder);
fakeFolder.remoteModifier().insert(barFileRootFolder);
-
- const auto lockedFileDavProps = QByteArray("<nc:lock>1</nc:lock>"
- "<nc:lock-owner-type>0</nc:lock-owner-type>"
- "<nc:lock-owner>user1</nc:lock-owner>"
- "<nc:lock-owner-displayname>user1</nc:lock-owner-displayname>"
- "<nc:lock-owner-editor>user1</nc:lock-owner-editor>"
- "<nc:lock-time>1648046707</nc:lock-time>");
-
- fakeFolder.remoteModifier().find("bar")->extraDavProperties = lockedFileDavProps;
+ fakeFolder.remoteModifier().modifyLockState(QStringLiteral("bar"), FileInfo::LockState::FileLocked, 0, QStringLiteral("user1"), {}, QStringLiteral("user1"), 1648046707, 0);
fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
fakeFolder.remoteModifier().insert(fooFileSubFolder);
QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("bar"), &fileRecordBefore));
QVERIFY(fileRecordBefore._lockstate._locked);
- const auto unlockedFileDavProps = QByteArray("<nc:lock>0</nc:lock>");
- fakeFolder.remoteModifier().find("bar")->extraDavProperties = unlockedFileDavProps;
+ fakeFolder.remoteModifier().modifyLockState(QStringLiteral("bar"), FileInfo::LockState::FileUnlocked, {}, {}, {}, {}, {}, {});
fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem);
#include "common/syncjournaldb.h"
#include "common/syncjournalfilerecord.h"
#include "syncenginetestutils.h"
+#include "localdiscoverytracker.h"
#include <QTest>
#include <QSignalSpy>
QVERIFY(jobFailure.wait());
QCOMPARE(jobSuccess.count(), 0);
}
+
+ void testSyncLockedFilesAlmostExpired()
+ {
+ FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ QSignalSpy spySyncCompleted(&fakeFolder.syncEngine(), &OCC::SyncEngine::finished);
+
+ ItemCompletedSpy completeSpy(fakeFolder);
+
+ completeSpy.clear();
+ QVERIFY(fakeFolder.syncOnce());
+
+ QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem);
+ OCC::SyncJournalFileRecord fileRecordBefore;
+ QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore));
+ QVERIFY(!fileRecordBefore._lockstate._locked);
+
+ fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch() - 1220, 1226);
+
+ completeSpy.clear();
+ QVERIFY(fakeFolder.syncOnce());
+
+ QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem);
+ OCC::SyncJournalFileRecord fileRecordLocked;
+ QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked));
+ QVERIFY(fileRecordLocked._lockstate._locked);
+
+ spySyncCompleted.clear();
+
+ QTest::qWait(5000);
+
+ fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileUnlocked, {}, {}, {}, {}, {}, {});
+
+ QCOMPARE(spySyncCompleted.count(), 0);
+ QVERIFY(spySyncCompleted.wait(3000));
+ QCOMPARE(spySyncCompleted.count(), 1);
+
+ OCC::SyncJournalFileRecord fileRecordUnlocked;
+ QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordUnlocked));
+ QVERIFY(!fileRecordUnlocked._lockstate._locked);
+ }
+
+ void testSyncLockedFilesNoExpiredLockedFiles()
+ {
+ FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ QSignalSpy spySyncCompleted(&fakeFolder.syncEngine(), &OCC::SyncEngine::finished);
+
+ ItemCompletedSpy completeSpy(fakeFolder);
+
+ completeSpy.clear();
+ QVERIFY(fakeFolder.syncOnce());
+
+ QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem);
+ OCC::SyncJournalFileRecord fileRecordBefore;
+ QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore));
+ QVERIFY(!fileRecordBefore._lockstate._locked);
+
+ fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch() - 1220, 1226);
+
+ completeSpy.clear();
+ QVERIFY(fakeFolder.syncOnce());
+
+ QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem);
+ OCC::SyncJournalFileRecord fileRecordLocked;
+ QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked));
+ QVERIFY(fileRecordLocked._lockstate._locked);
+
+ completeSpy.clear();
+ QVERIFY(fakeFolder.syncOnce());
+
+ spySyncCompleted.clear();
+
+ QTest::qWait(1000);
+
+ fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileUnlocked, {}, {}, {}, {}, {}, {});
+
+ QCOMPARE(spySyncCompleted.count(), 0);
+ QVERIFY(!spySyncCompleted.wait(3000));
+ QCOMPARE(spySyncCompleted.count(), 0);
+
+ OCC::SyncJournalFileRecord fileRecordUnlocked;
+ QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordUnlocked));
+ QVERIFY(fileRecordUnlocked._lockstate._locked);
+ }
};
QTEST_GUILESS_MAIN(TestLockFile)