Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>2022-04-08 11:23:50 +0300
committerMatthieu Gallien <matthieu_gallien@yahoo.fr>2022-05-02 14:52:05 +0300
commitdc95f2916557d5215b827d9d75ddfa8708669302 (patch)
tree015a2a31f7dbcd7bb83a11a65b05aa42508821fe
parent860d7dd9de11dcf28f2f7b01b6bf560a1fcd31f1 (diff)
fetch and store in sync database information abot lock state of files
fetch lock properties from server decode them and store them in sync database test to ensure we do properly handle those properties Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
-rw-r--r--src/common/syncjournaldb.cpp143
-rw-r--r--src/common/syncjournalfilerecord.h11
-rw-r--r--src/libsync/discovery.cpp13
-rw-r--r--src/libsync/discoveryphase.cpp48
-rw-r--r--src/libsync/discoveryphase.h8
-rw-r--r--src/libsync/propagatedownload.cpp6
-rw-r--r--src/libsync/syncfileitem.cpp14
-rw-r--r--src/libsync/syncfileitem.h23
-rw-r--r--test/syncenginetestutils.cpp2
-rw-r--r--test/testlocaldiscovery.cpp49
10 files changed, 233 insertions, 84 deletions
diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp
index ca5ae1faa..b5e4f97ca 100644
--- a/src/common/syncjournaldb.cpp
+++ b/src/common/syncjournaldb.cpp
@@ -48,7 +48,8 @@ Q_LOGGING_CATEGORY(lcDb, "nextcloud.sync.database", QtInfoMsg)
#define GET_FILE_RECORD_QUERY \
"SELECT path, inode, modtime, type, md5, fileid, remotePerm, filesize," \
- " ignoredChildrenRemote, contentchecksumtype.name || ':' || contentChecksum, e2eMangledName, isE2eEncrypted " \
+ " ignoredChildrenRemote, contentchecksumtype.name || ':' || contentChecksum, e2eMangledName, isE2eEncrypted, " \
+ " lock, lockOwnerDisplayName, lockOwnerId, lockType, lockOwnerEditor, lockTime, lockTimeout " \
" FROM metadata" \
" LEFT JOIN checksumtype as contentchecksumtype ON metadata.contentChecksumTypeId == contentchecksumtype.id"
@@ -66,6 +67,13 @@ static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &que
rec._checksumHeader = query.baValue(9);
rec._e2eMangledName = query.baValue(10);
rec._isE2eEncrypted = query.intValue(11) > 0;
+ rec._lockstate._locked = query.intValue(12) > 0;
+ rec._lockstate._lockOwnerDisplayName = query.stringValue(13);
+ rec._lockstate._lockOwnerId = query.stringValue(14);
+ rec._lockstate._lockOwnerType = query.int64Value(15);
+ rec._lockstate._lockEditorApp = query.stringValue(16);
+ rec._lockstate._lockTime = query.int64Value(17);
+ rec._lockstate._lockTimeout = query.int64Value(18);
}
static QByteArray defaultJournalMode(const QString &dbPath)
@@ -658,39 +666,31 @@ bool SyncJournalDb::updateMetadataTableStructure()
return false;
}
- if (columns.indexOf("fileid") == -1) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN fileid VARCHAR(128);");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: Add column fileid"), query);
- re = false;
- }
+ const auto addColumn = [this, &columns, &re] (const QString &columnName, const QString &dataType, const bool withIndex = false) {
+ const auto latin1ColumnName = columnName.toLatin1();
+ if (columns.indexOf(latin1ColumnName) == -1) {
+ SqlQuery query(_db);
+ const auto request = QStringLiteral("ALTER TABLE metadata ADD COLUMN %1 %2;").arg(columnName).arg(dataType);
+ query.prepare(request.toLatin1());
+ if (!query.exec()) {
+ sqlFail(QStringLiteral("updateMetadataTableStructure: add %1 column").arg(columnName), query);
+ re = false;
+ }
- query.prepare("CREATE INDEX metadata_file_id ON metadata(fileid);");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: create index fileid"), query);
- re = false;
- }
- commitInternal(QStringLiteral("update database structure: add fileid col"));
- }
- if (columns.indexOf("remotePerm") == -1) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN remotePerm VARCHAR(128);");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: add column remotePerm"), query);
- re = false;
- }
- commitInternal(QStringLiteral("update database structure (remotePerm)"));
- }
- if (columns.indexOf("filesize") == -1) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN filesize BIGINT;");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateDatabaseStructure: add column filesize"), query);
- re = false;
+ if (withIndex) {
+ query.prepare(QStringLiteral("CREATE INDEX metadata_%1 ON metadata(%1);").arg(columnName).toLatin1());
+ if (!query.exec()) {
+ sqlFail(QStringLiteral("updateMetadataTableStructure: create index %1").arg(columnName), query);
+ re = false;
+ }
+ }
+ commitInternal(QStringLiteral("update database structure: add %1 column").arg(columnName));
}
- commitInternal(QStringLiteral("update database structure: add filesize col"));
- }
+ };
+
+ addColumn(QStringLiteral("fileid"), QStringLiteral("VARCHAR(128)"), true);
+ addColumn(QStringLiteral("remotePerm"), QStringLiteral("VARCHAR(128)"));
+ addColumn(QStringLiteral("filesize"), QStringLiteral("BIGINT"));
if (true) {
SqlQuery query(_db);
@@ -722,54 +722,11 @@ bool SyncJournalDb::updateMetadataTableStructure()
commitInternal(QStringLiteral("update database structure: add parent index"));
}
- if (columns.indexOf("ignoredChildrenRemote") == -1) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN ignoredChildrenRemote INT;");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: add ignoredChildrenRemote column"), query);
- re = false;
- }
- commitInternal(QStringLiteral("update database structure: add ignoredChildrenRemote col"));
- }
-
- if (columns.indexOf("contentChecksum") == -1) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN contentChecksum TEXT;");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: add contentChecksum column"), query);
- re = false;
- }
- commitInternal(QStringLiteral("update database structure: add contentChecksum col"));
- }
- if (columns.indexOf("contentChecksumTypeId") == -1) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN contentChecksumTypeId INTEGER;");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: add contentChecksumTypeId column"), query);
- re = false;
- }
- commitInternal(QStringLiteral("update database structure: add contentChecksumTypeId col"));
- }
-
- if (!columns.contains("e2eMangledName")) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN e2eMangledName TEXT;");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: add e2eMangledName column"), query);
- re = false;
- }
- commitInternal(QStringLiteral("update database structure: add e2eMangledName col"));
- }
-
- if (!columns.contains("isE2eEncrypted")) {
- SqlQuery query(_db);
- query.prepare("ALTER TABLE metadata ADD COLUMN isE2eEncrypted INTEGER;");
- if (!query.exec()) {
- sqlFail(QStringLiteral("updateMetadataTableStructure: add isE2eEncrypted column"), query);
- re = false;
- }
- commitInternal(QStringLiteral("update database structure: add isE2eEncrypted col"));
- }
+ addColumn(QStringLiteral("ignoredChildrenRemote"), QStringLiteral("INT"));
+ addColumn(QStringLiteral("contentChecksum"), QStringLiteral("TEXT"));
+ addColumn(QStringLiteral("contentChecksumTypeId"), QStringLiteral("INTEGER"));
+ addColumn(QStringLiteral("e2eMangledName"), QStringLiteral("TEXT"));
+ addColumn(QStringLiteral("isE2eEncrypted"), QStringLiteral("INTEGER"));
auto uploadInfoColumns = tableColumns("uploadinfo");
if (uploadInfoColumns.isEmpty())
@@ -806,6 +763,14 @@ bool SyncJournalDb::updateMetadataTableStructure()
commitInternal(QStringLiteral("update database structure: add e2eMangledName index"));
}
+ addColumn(QStringLiteral("lock"), QStringLiteral("INTEGER"));
+ addColumn(QStringLiteral("lockType"), QStringLiteral("INTEGER"));
+ addColumn(QStringLiteral("lockOwnerDisplayName"), QStringLiteral("TEXT"));
+ addColumn(QStringLiteral("lockOwnerId"), QStringLiteral("TEXT"));
+ addColumn(QStringLiteral("lockOwnerEditor"), QStringLiteral("TEXT"));
+ addColumn(QStringLiteral("lockTime"), QStringLiteral("INTEGER"));
+ addColumn(QStringLiteral("lockTimeout"), QStringLiteral("INTEGER"));
+
return re;
}
@@ -919,7 +884,10 @@ Result<void, QString> SyncJournalDb::setFileRecord(const SyncJournalFileRecord &
<< "modtime:" << record._modtime << "type:" << record._type
<< "etag:" << record._etag << "fileId:" << record._fileId << "remotePerm:" << record._remotePerm.toString()
<< "fileSize:" << record._fileSize << "checksum:" << record._checksumHeader
- << "e2eMangledName:" << record.e2eMangledName() << "isE2eEncrypted:" << record._isE2eEncrypted;
+ << "e2eMangledName:" << record.e2eMangledName() << "isE2eEncrypted:" << record._isE2eEncrypted
+ << "lock:" << (record._lockstate._locked ? "true" : "false") << "lock owner type:" << record._lockstate._lockOwnerType
+ << "lock owner:" << record._lockstate._lockOwnerDisplayName << "lock owner id:" << record._lockstate._lockOwnerId
+ << "lock editor:" << record._lockstate._lockEditorApp;
const qint64 phash = getPHash(record._path);
if (!checkConnect()) {
@@ -943,8 +911,10 @@ Result<void, QString> SyncJournalDb::setFileRecord(const SyncJournalFileRecord &
int contentChecksumTypeId = mapChecksumType(checksumType);
const auto query = _queryManager.get(PreparedSqlQueryManager::SetFileRecordQuery, QByteArrayLiteral("INSERT OR REPLACE INTO metadata "
- "(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, contentChecksum, contentChecksumTypeId, e2eMangledName, isE2eEncrypted) "
- "VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18);"),
+ "(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, "
+ "contentChecksum, contentChecksumTypeId, e2eMangledName, isE2eEncrypted, lock, lockType, lockOwnerDisplayName, lockOwnerId, "
+ "lockOwnerEditor, lockTime, lockTimeout) "
+ "VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25);"),
_db);
if (!query) {
return query->error();
@@ -968,6 +938,13 @@ Result<void, QString> SyncJournalDb::setFileRecord(const SyncJournalFileRecord &
query->bindValue(16, contentChecksumTypeId);
query->bindValue(17, record._e2eMangledName);
query->bindValue(18, record._isE2eEncrypted);
+ query->bindValue(19, record._lockstate._locked ? 1 : 0);
+ query->bindValue(20, record._lockstate._lockOwnerType);
+ query->bindValue(21, record._lockstate._lockOwnerDisplayName);
+ query->bindValue(22, record._lockstate._lockOwnerId);
+ query->bindValue(23, record._lockstate._lockEditorApp);
+ query->bindValue(24, record._lockstate._lockTime);
+ query->bindValue(25, record._lockstate._lockTimeout);
if (!query->exec()) {
return query->error();
diff --git a/src/common/syncjournalfilerecord.h b/src/common/syncjournalfilerecord.h
index b47f98e5b..9f3e0b518 100644
--- a/src/common/syncjournalfilerecord.h
+++ b/src/common/syncjournalfilerecord.h
@@ -31,6 +31,16 @@ namespace OCC {
class SyncFileItem;
+struct SyncJournalFileLockInfo {
+ bool _locked = false;
+ QString _lockOwnerDisplayName;
+ QString _lockOwnerId;
+ qint64 _lockOwnerType = 0;
+ QString _lockEditorApp;
+ qint64 _lockTime = 0;
+ qint64 _lockTimeout = 0;
+};
+
/**
* @brief The SyncJournalFileRecord class
* @ingroup libsync
@@ -70,6 +80,7 @@ public:
QByteArray _checksumHeader;
QByteArray _e2eMangledName;
bool _isE2eEncrypted = false;
+ SyncJournalFileLockInfo _lockstate;
};
bool OCSYNC_EXPORT
diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp
index 63b75a05f..7527d06d5 100644
--- a/src/libsync/discovery.cpp
+++ b/src/libsync/discovery.cpp
@@ -363,6 +363,8 @@ void ProcessDirectoryJob::processFile(PathTuple path,
{
const char *hasServer = serverEntry.isValid() ? "true" : _queryServer == ParentNotChanged ? "db" : "false";
const char *hasLocal = localEntry.isValid() ? "true" : _queryLocal == ParentNotChanged ? "db" : "false";
+ const auto serverFileIsLocked = serverEntry.locked == SyncFileItem::LockStatus::LockedItem ? "locked" : "not locked";
+ const auto localFileIsLocked = dbEntry._lockstate._locked ? "locked" : "not locked";
qCInfo(lcDisco).nospace() << "Processing " << path._original
<< " | valid: " << dbEntry.isValid() << "/" << hasLocal << "/" << hasServer
<< " | mtime: " << dbEntry._modtime << "/" << localEntry.modtime << "/" << serverEntry.modtime
@@ -374,7 +376,8 @@ void ProcessDirectoryJob::processFile(PathTuple path,
<< " | inode: " << dbEntry._inode << "/" << localEntry.inode << "/"
<< " | type: " << dbEntry._type << "/" << localEntry.type << "/" << (serverEntry.isDirectory ? ItemTypeDirectory : ItemTypeFile)
<< " | e2ee: " << dbEntry._isE2eEncrypted << "/" << serverEntry.isE2eEncrypted
- << " | e2eeMangledName: " << dbEntry.e2eMangledName() << "/" << serverEntry.e2eMangledName;
+ << " | e2eeMangledName: " << dbEntry.e2eMangledName() << "/" << serverEntry.e2eMangledName
+ << " | file lock: " << localFileIsLocked << "//" << serverFileIsLocked;
if (localEntry.isValid()
&& !serverEntry.isValid()
@@ -483,6 +486,14 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
Q_ASSERT(serverEntry.e2eMangledName.startsWith(rootPath));
return serverEntry.e2eMangledName.mid(rootPath.length());
}();
+ item->_locked = serverEntry.locked;
+ item->_lockOwnerDisplayName = serverEntry.lockOwnerDisplayName;
+ item->_lockOwnerId = serverEntry.lockOwnerId;
+ item->_lockOwnerType = serverEntry.lockOwnerType;
+ item->_lockEditorApp = serverEntry.lockEditorApp;
+ item->_lockTime = serverEntry.lockTime;
+ item->_lockTimeout = serverEntry.lockTimeout;
+ qCInfo(lcDisco()) << item->_locked << item->_lockOwnerDisplayName << item->_lockOwnerId << item->_lockOwnerType << item->_lockEditorApp << item->_lockTime << item->_lockTimeout;
// Check for missing server data
{
diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp
index d71dca433..67b91ad87 100644
--- a/src/libsync/discoveryphase.cpp
+++ b/src/libsync/discoveryphase.cpp
@@ -378,6 +378,15 @@ void DiscoverySingleDirectoryJob::start()
if (_account->capabilities().clientSideEncryptionAvailable()) {
props << "http://nextcloud.org/ns:is-encrypted";
}
+ if (_account->capabilities().filesLockAvailable()) {
+ props << "http://nextcloud.org/ns:lock"
+ << "http://nextcloud.org/ns:lock-owner-displayname"
+ << "http://nextcloud.org/ns:lock-owner"
+ << "http://nextcloud.org/ns:lock-owner-type"
+ << "http://nextcloud.org/ns:lock-owner-editor"
+ << "http://nextcloud.org/ns:lock-time"
+ << "http://nextcloud.org/ns:lock-timeout";
+ }
lsColJob->setProperties(props);
@@ -445,7 +454,46 @@ static void propertyMapToRemoteInfo(const QMap<QString, QString> &map, RemoteInf
}
} else if (property == "is-encrypted" && value == QStringLiteral("1")) {
result.isE2eEncrypted = true;
+ } else if (property == "lock") {
+ result.locked = (value == QStringLiteral("1") ? SyncFileItem::LockStatus::LockedItem : SyncFileItem::LockStatus::UnlockedItem);
+ }
+ if (property == "lock-owner-displayname") {
+ result.lockOwnerDisplayName = value;
+ }
+ if (property == "lock-owner") {
+ result.lockOwnerId = value;
+ }
+ if (property == "lock-owner-type") {
+ auto ok = false;
+ const auto intConvertedValue = value.toULongLong(&ok);
+ if (ok) {
+ result.lockOwnerType = static_cast<SyncFileItem::LockOwnerType>(intConvertedValue);
+ } else {
+ result.lockOwnerType = SyncFileItem::LockOwnerType::UserLock;
+ }
+ }
+ if (property == "lock-owner-editor") {
+ result.lockEditorApp = value;
}
+ if (property == "lock-time") {
+ auto ok = false;
+ const auto intConvertedValue = value.toULongLong(&ok);
+ if (ok) {
+ result.lockTime = intConvertedValue;
+ } else {
+ result.lockTime = 0;
+ }
+ }
+ if (property == "lock-timeout") {
+ auto ok = false;
+ const auto intConvertedValue = value.toULongLong(&ok);
+ if (ok) {
+ result.lockTimeout = intConvertedValue;
+ } else {
+ result.lockTimeout = 0;
+ }
+ }
+
}
if (result.isDirectory && map.contains("size")) {
diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h
index cd210a644..3f5240dd1 100644
--- a/src/libsync/discoveryphase.h
+++ b/src/libsync/discoveryphase.h
@@ -65,6 +65,14 @@ struct RemoteInfo
QString directDownloadUrl;
QString directDownloadCookies;
+
+ SyncFileItem::LockStatus locked = SyncFileItem::LockStatus::UnlockedItem;
+ QString lockOwnerDisplayName;
+ QString lockOwnerId;
+ SyncFileItem::LockOwnerType lockOwnerType = SyncFileItem::LockOwnerType::UserLock;
+ QString lockEditorApp;
+ qint64 lockTime = 0;
+ qint64 lockTimeout = 0;
};
struct LocalInfo
diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp
index bc2922070..57e5572de 100644
--- a/src/libsync/propagatedownload.cpp
+++ b/src/libsync/propagatedownload.cpp
@@ -1211,6 +1211,12 @@ void PropagateDownloadFile::downloadFinished()
return;
}
+ qCInfo(lcPropagateDownload()) << propagator()->account()->davUser() << propagator()->account()->davDisplayName() << propagator()->account()->displayName();
+ if (_item->_locked == SyncFileItem::LockStatus::LockedItem && (_item->_lockOwnerType != SyncFileItem::LockOwnerType::UserLock || _item->_lockOwnerId != propagator()->account()->davUser())) {
+ qCInfo(lcPropagateDownload()) << "file is locked: making it read only";
+ FileSystem::setFileReadOnly(fn, true);
+ }
+
FileSystem::setFileHidden(fn, false);
// Maybe we downloaded a newer version of the file than we thought we would...
diff --git a/src/libsync/syncfileitem.cpp b/src/libsync/syncfileitem.cpp
index cc7afe0f0..07202d41f 100644
--- a/src/libsync/syncfileitem.cpp
+++ b/src/libsync/syncfileitem.cpp
@@ -45,6 +45,13 @@ SyncJournalFileRecord SyncFileItem::toSyncJournalFileRecordWithInode(const QStri
rec._checksumHeader = _checksumHeader;
rec._e2eMangledName = _encryptedFileName.toUtf8();
rec._isE2eEncrypted = _isEncrypted;
+ rec._lockstate._locked = _locked == LockStatus::LockedItem;
+ rec._lockstate._lockOwnerDisplayName = _lockOwnerDisplayName;
+ rec._lockstate._lockOwnerId = _lockOwnerId;
+ rec._lockstate._lockOwnerType = static_cast<qint64>(_lockOwnerType);
+ rec._lockstate._lockEditorApp = _lockEditorApp;
+ rec._lockstate._lockTime = _lockTime;
+ rec._lockstate._lockTimeout = _lockTimeout;
// Update the inode if possible
rec._inode = _inode;
@@ -75,6 +82,13 @@ SyncFileItemPtr SyncFileItem::fromSyncJournalFileRecord(const SyncJournalFileRec
item->_checksumHeader = rec._checksumHeader;
item->_encryptedFileName = rec.e2eMangledName();
item->_isEncrypted = rec._isE2eEncrypted;
+ item->_locked = rec._lockstate._locked ? LockStatus::LockedItem : LockStatus::UnlockedItem;
+ item->_lockOwnerDisplayName = rec._lockstate._lockOwnerDisplayName;
+ item->_lockOwnerId = rec._lockstate._lockOwnerId;
+ item->_lockOwnerType = static_cast<LockOwnerType>(rec._lockstate._lockOwnerType);
+ item->_lockEditorApp = rec._lockstate._lockEditorApp;
+ item->_lockTime = rec._lockstate._lockTime;
+ item->_lockTimeout = rec._lockstate._lockTimeout;
return item;
}
diff --git a/src/libsync/syncfileitem.h b/src/libsync/syncfileitem.h
index a38b4e94f..9dc6fa4f5 100644
--- a/src/libsync/syncfileitem.h
+++ b/src/libsync/syncfileitem.h
@@ -93,6 +93,21 @@ public:
};
Q_ENUM(Status)
+ enum class LockStatus {
+ UnlockedItem = 0,
+ LockedItem = 1,
+ };
+
+ Q_ENUM(LockStatus)
+
+ enum class LockOwnerType : int{
+ UserLock = 0,
+ AppLock = 1,
+ TokenLock = 2,
+ };
+
+ Q_ENUM(LockOwnerType)
+
SyncJournalFileRecord toSyncJournalFileRecordWithInode(const QString &localFileName) const;
/** Creates a basic SyncFileItem from a DB record
@@ -278,6 +293,14 @@ public:
QString _directDownloadUrl;
QString _directDownloadCookies;
+
+ LockStatus _locked = LockStatus::UnlockedItem;
+ QString _lockOwnerId;
+ QString _lockOwnerDisplayName;
+ LockOwnerType _lockOwnerType = LockOwnerType::UserLock;
+ QString _lockEditorApp;
+ qint64 _lockTime = 0;
+ qint64 _lockTimeout = 0;
};
inline bool operator<(const SyncFileItemPtr &item1, const SyncFileItemPtr &item2)
diff --git a/test/syncenginetestutils.cpp b/test/syncenginetestutils.cpp
index 1d813e3ff..86873ca8f 100644
--- a/test/syncenginetestutils.cpp
+++ b/test/syncenginetestutils.cpp
@@ -298,11 +298,13 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
// Don't care about the request and just return a full propfind
const QString davUri { QStringLiteral("DAV:") };
const QString ocUri { QStringLiteral("http://owncloud.org/ns") };
+ const QString ncUri { QStringLiteral("http://nextcloud.org/ns") };
QBuffer buffer { &payload };
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter xml(&buffer);
xml.writeNamespace(davUri, QStringLiteral("d"));
xml.writeNamespace(ocUri, QStringLiteral("oc"));
+ xml.writeNamespace(ncUri, QStringLiteral("nc"));
xml.writeStartDocument();
xml.writeStartElement(davUri, QStringLiteral("multistatus"));
auto writeFileResponse = [&](const FileInfo &fileInfo) {
diff --git a/test/testlocaldiscovery.cpp b/test/testlocaldiscovery.cpp
index d84f95345..c3719d0fc 100644
--- a/test/testlocaldiscovery.cpp
+++ b/test/testlocaldiscovery.cpp
@@ -594,6 +594,55 @@ private slots:
auto expectedState = fakeFolder.currentLocalState();
QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
}
+
+ void testDiscoverLockChanges()
+ {
+// Logger::instance()->setLogDebug(true);
+
+ FakeFolder fakeFolder{FileInfo{}};
+ fakeFolder.syncEngine().account()->setCapabilities({{"activity", QVariantMap{{"apiv2", QVariantList{"filters", "filters-api", "previews", "rich-strings"}}}},
+ {"bruteforce", QVariantMap{{"delay", 0}}},
+ {"core", QVariantMap{{"pollinterval", 60}, {"webdav-root", "remote.php/webdav"}}},
+ {"dav", QVariantMap{{"chunking", "1.0"}}},
+ {"files", QVariantMap{{"bigfilechunking", true}, {"blacklisted_files", QVariantList{".htaccess"}},
+ {"comments", true},
+ {"directEditing", QVariantMap{{"etag", "c748e8fc588b54fc5af38c4481a19d20"}, {"url", "https://nextcloud.local/ocs/v2.php/apps/files/api/v1/directEditing"}}},
+ {"locking", "1.0"}}}});
+
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ const QString fooFileRootFolder("foo");
+ const QString barFileRootFolder("bar");
+ const QString fooFileSubFolder("subfolder/foo");
+ const QString barFileSubFolder("subfolder/bar");
+ const QString fooFileAaaSubFolder("aaa/subfolder/foo");
+ const QString barFileAaaSubFolder("aaa/subfolder/bar");
+
+ fakeFolder.remoteModifier().insert(fooFileRootFolder);
+
+ fakeFolder.remoteModifier().insert(barFileRootFolder);
+ fakeFolder.remoteModifier().find("bar")->extraDavProperties = "<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><oc:size>20020</oc:size>";
+
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileSubFolder);
+ fakeFolder.remoteModifier().insert(barFileSubFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
+ fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ fakeFolder.remoteModifier().find("bar")->extraDavProperties = "<nc:lock>0</nc:lock>";
+
+ fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem);
+ QVERIFY(fakeFolder.syncOnce());
+ }
};
QTEST_GUILESS_MAIN(TestLocalDiscovery)