diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index 9ec575b08..5ed80a643 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -49,7 +49,7 @@ 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, " \ - " lock, lockOwnerDisplayName, lockOwnerId, lockType, lockOwnerEditor, lockTime, lockTimeout, isShared, lastShareStateFetchedTimestmap, sharedByMe" \ + " lock, lockOwnerDisplayName, lockOwnerId, lockType, lockOwnerEditor, lockTime, lockTimeout, lockToken, isShared, lastShareStateFetchedTimestmap, sharedByMe" \ " FROM metadata" \ " LEFT JOIN checksumtype as contentchecksumtype ON metadata.contentChecksumTypeId == contentchecksumtype.id" @@ -74,9 +74,10 @@ static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &que rec._lockstate._lockEditorApp = query.stringValue(16); rec._lockstate._lockTime = query.int64Value(17); rec._lockstate._lockTimeout = query.int64Value(18); - rec._isShared = query.intValue(19) > 0; - rec._lastShareStateFetchedTimestamp = query.int64Value(20); - rec._sharedByMe = query.intValue(21) > 0; + rec._lockstate._lockToken = query.stringValue(19); + rec._isShared = query.intValue(20) > 0; + rec._lastShareStateFetchedTimestamp = query.int64Value(21); + rec._sharedByMe = query.intValue(22) > 0; } static QByteArray defaultJournalMode(const QString &dbPath) @@ -826,6 +827,7 @@ bool SyncJournalDb::updateMetadataTableStructure() addColumn(QStringLiteral("lockOwnerEditor"), QStringLiteral("TEXT")); addColumn(QStringLiteral("lockTime"), QStringLiteral("INTEGER")); addColumn(QStringLiteral("lockTimeout"), QStringLiteral("INTEGER")); + addColumn(QStringLiteral("lockToken"), QStringLiteral("TEXT")); SqlQuery query(_db); query.prepare("CREATE INDEX IF NOT EXISTS caseconflicts_basePath ON caseconflicts(basePath);"); @@ -987,8 +989,8 @@ Result SyncJournalDb::setFileRecord(const SyncJournalFileRecord & 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, lock, lockType, lockOwnerDisplayName, lockOwnerId, " - "lockOwnerEditor, lockTime, lockTimeout, isShared, lastShareStateFetchedTimestmap, sharedByMe) " - "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, ?26, ?27, ?28);"), + "lockOwnerEditor, lockTime, lockTimeout, lockToken, isShared, lastShareStateFetchedTimestmap, sharedByMe) " + "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, ?26, ?27, ?28, ?29);"), _db); if (!query) { qCDebug(lcDb) << "database error:" << query->error(); @@ -1020,9 +1022,10 @@ Result SyncJournalDb::setFileRecord(const SyncJournalFileRecord & query->bindValue(23, record._lockstate._lockEditorApp); query->bindValue(24, record._lockstate._lockTime); query->bindValue(25, record._lockstate._lockTimeout); - query->bindValue(26, record._isShared); - query->bindValue(27, record._lastShareStateFetchedTimestamp); - query->bindValue(28, record._sharedByMe); + query->bindValue(26, record._lockstate._lockToken); + query->bindValue(27, record._isShared); + query->bindValue(28, record._lastShareStateFetchedTimestamp); + query->bindValue(29, record._sharedByMe); if (!query->exec()) { qCDebug(lcDb) << "database error:" << query->error(); @@ -1616,7 +1619,7 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename, const auto query = _queryManager.get(PreparedSqlQueryManager::SetFileRecordLocalMetadataQuery, QByteArrayLiteral("UPDATE metadata" " SET inode=?2, modtime=?3, filesize=?4, lock=?5, lockType=?6," " lockOwnerDisplayName=?7, lockOwnerId=?8, lockOwnerEditor = ?9," - " lockTime=?10, lockTimeout=?11" + " lockTime=?10, lockTimeout=?11, lockToken=?12" " WHERE phash == ?1;"), _db); if (!query) { @@ -1635,6 +1638,7 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename, query->bindValue(9, lockInfo._lockEditorApp); query->bindValue(10, lockInfo._lockTime); query->bindValue(11, lockInfo._lockTimeout); + query->bindValue(12, lockInfo._lockToken); if (!query->exec()) { qCDebug(lcDb) << "database error:" << query->error(); return false; diff --git a/src/common/syncjournalfilerecord.h b/src/common/syncjournalfilerecord.h index 7270fac13..c7321c15f 100644 --- a/src/common/syncjournalfilerecord.h +++ b/src/common/syncjournalfilerecord.h @@ -39,6 +39,7 @@ struct SyncJournalFileLockInfo { QString _lockEditorApp; qint64 _lockTime = 0; qint64 _lockTimeout = 0; + QString _lockToken; }; /** diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 6eb2eefd7..0a259360f 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -712,6 +712,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it item->_lockEditorApp = serverEntry.lockEditorApp; item->_lockTime = serverEntry.lockTime; item->_lockTimeout = serverEntry.lockTimeout; + item->_lockToken = serverEntry.lockToken; qCDebug(lcDisco()) << "item lock for:" << item->_file << item->_locked @@ -720,7 +721,8 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it << item->_lockOwnerType << item->_lockEditorApp << item->_lockTime - << item->_lockTimeout; + << item->_lockTimeout + << item->_lockToken; // Check for missing server data { diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index ca96b8c82..606f34f35 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -415,7 +415,8 @@ void DiscoverySingleDirectoryJob::start() << "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"; + << "http://nextcloud.org/ns:lock-timeout" + << "http://nextcloud.org/ns:lock-token"; } props << "http://nextcloud.org/ns:is-mount-root"; @@ -547,7 +548,9 @@ static void propertyMapToRemoteInfo(const QMap &map, RemotePer result.lockTimeout = 0; } } - + if (property == "lock-token") { + result.lockToken = value; + } } if (result.isDirectory && map.contains("size")) { diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 7d53f1336..ff38d7391 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -86,6 +86,7 @@ struct RemoteInfo QString lockEditorApp; qint64 lockTime = 0; qint64 lockTimeout = 0; + QString lockToken; }; struct LocalInfo diff --git a/src/libsync/lockfilejobs.cpp b/src/libsync/lockfilejobs.cpp index aa4b8af42..77e48afcb 100644 --- a/src/libsync/lockfilejobs.cpp +++ b/src/libsync/lockfilejobs.cpp @@ -115,6 +115,7 @@ void LockFileJob::setFileRecordLocked(SyncJournalFileRecord &record) const record._lockstate._lockEditorApp = _editorName; record._lockstate._lockTime = _lockTime; record._lockstate._lockTimeout = _lockTimeout; + record._lockstate._lockToken = _lockToken; if (!_etag.isEmpty()) { record._etag = _etag; } @@ -129,6 +130,7 @@ void LockFileJob::resetState() _userId.clear(); _lockTime = 0; _lockTimeout = 0; + _lockToken.clear(); } SyncJournalFileRecord LockFileJob::handleReply() @@ -241,6 +243,8 @@ void LockFileJob::decodeStartElement(const QString &name, _editorName = reader.readElementText(); } else if (name == QStringLiteral("getetag")) { _etag = reader.readElementText().toUtf8(); + } else if (name == QStringLiteral("lock-token")) { + _lockToken = reader.readElementText(); } } diff --git a/src/libsync/lockfilejobs.h b/src/libsync/lockfilejobs.h index 8b2e287f6..9b8be46c5 100644 --- a/src/libsync/lockfilejobs.h +++ b/src/libsync/lockfilejobs.h @@ -59,6 +59,7 @@ private: QByteArray _etag; qint64 _lockTime = 0; qint64 _lockTimeout = 0; + QString _lockToken; QString _remoteSyncPathWithTrailingSlash; QString _localSyncPath; }; diff --git a/src/libsync/propagateuploadng.cpp b/src/libsync/propagateuploadng.cpp index f778f79ef..a67225d2a 100644 --- a/src/libsync/propagateuploadng.cpp +++ b/src/libsync/propagateuploadng.cpp @@ -328,6 +328,9 @@ void PropagateUploadFileNG::finishUpload() const auto fileSize = _fileToUpload._size; headers[QByteArrayLiteral("OC-Total-Length")] = QByteArray::number(fileSize); + if (_item->_locked == SyncFileItem::LockStatus::LockedItem) { + headers[QByteArrayLiteral("If")] = (QLatin1String("<") + propagator()->account()->davUrl().toString() + _fileToUpload._file + "> (_lockToken.toUtf8() + ">)").toUtf8(); + } const auto job = new MoveJob(propagator()->account(), Utility::concatUrlPath(chunkUploadFolderUrl(), "/.file"), destination, headers, this); _jobs.append(job); diff --git a/src/libsync/propagateuploadv1.cpp b/src/libsync/propagateuploadv1.cpp index a32e796a5..7467b2ce3 100644 --- a/src/libsync/propagateuploadv1.cpp +++ b/src/libsync/propagateuploadv1.cpp @@ -102,6 +102,10 @@ void PropagateUploadFileV1::startNextChunk() QString path = _fileToUpload._file; + if (_item->_locked == SyncFileItem::LockStatus::LockedItem) { + headers[QByteArrayLiteral("If")] = (QLatin1String("<") + propagator()->account()->davUrl().toString() + _fileToUpload._file + "> (_lockToken.toUtf8() + ">)").toUtf8(); + } + qint64 chunkStart = 0; qint64 currentChunkSize = fileSize; bool isFinalChunk = false; diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 9be7992a5..61b6f0c5a 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -440,6 +440,7 @@ void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item) lockInfo._lockOwnerType = static_cast(item->_lockOwnerType); lockInfo._lockOwnerDisplayName = item->_lockOwnerDisplayName; lockInfo._lockEditorApp = item->_lockOwnerDisplayName; + lockInfo._lockToken = item->_lockToken; if (!_journal->updateLocalMetadata(item->_file, item->_modtime, item->_size, item->_inode, lockInfo)) { qCWarning(lcEngine) << "Could not update local metadata for file" << item->_file; diff --git a/src/libsync/syncfileitem.cpp b/src/libsync/syncfileitem.cpp index 6bf10cd7a..4b71c7534 100644 --- a/src/libsync/syncfileitem.cpp +++ b/src/libsync/syncfileitem.cpp @@ -125,6 +125,7 @@ SyncJournalFileRecord SyncFileItem::toSyncJournalFileRecordWithInode(const QStri rec._lockstate._lockEditorApp = _lockEditorApp; rec._lockstate._lockTime = _lockTime; rec._lockstate._lockTimeout = _lockTimeout; + rec._lockstate._lockToken = _lockToken; // Update the inode if possible rec._inode = _inode; @@ -163,6 +164,7 @@ SyncFileItemPtr SyncFileItem::fromSyncJournalFileRecord(const SyncJournalFileRec item->_lockEditorApp = rec._lockstate._lockEditorApp; item->_lockTime = rec._lockstate._lockTime; item->_lockTimeout = rec._lockstate._lockTimeout; + item->_lockToken = rec._lockstate._lockToken; item->_sharedByMe = rec._sharedByMe; item->_isShared = rec._isShared; item->_lastShareStateFetchedTimestamp = rec._lastShareStateFetchedTimestamp; @@ -220,6 +222,8 @@ SyncFileItemPtr SyncFileItem::fromProperties(const QString &filePath, const QMap item->_lockTimeout = ok ? intConvertedValue : 0; } + item->_lockToken = properties.value(QStringLiteral("lock-token")); + const auto date = QDateTime::fromString(properties.value(QStringLiteral("getlastmodified")), Qt::RFC2822Date); Q_ASSERT(date.isValid()); if (date.toSecsSinceEpoch() > 0) { @@ -250,6 +254,7 @@ void SyncFileItem::updateLockStateFromDbRecord(const SyncJournalFileRecord &dbRe _lockEditorApp = dbRecord._lockstate._lockEditorApp; _lockTime = dbRecord._lockstate._lockTime; _lockTimeout = dbRecord._lockstate._lockTimeout; + _lockToken = dbRecord._lockstate._lockToken; } } diff --git a/src/libsync/syncfileitem.h b/src/libsync/syncfileitem.h index 3f6a52898..4bf481a13 100644 --- a/src/libsync/syncfileitem.h +++ b/src/libsync/syncfileitem.h @@ -326,6 +326,7 @@ public: QString _lockEditorApp; qint64 _lockTime = 0; qint64 _lockTimeout = 0; + QString _lockToken; bool _isShared = false; time_t _lastShareStateFetchedTimestamp = 0;