desktop/src/libsync/updatefiledropmetadata.cpp

215 lines
8.5 KiB
C++

/*
* Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "updatefiledropmetadata.h"
#include "account.h"
#include "clientsideencryptionjobs.h"
#include "clientsideencryption.h"
#include "syncfileitem.h"
#include <QLoggingCategory>
#include <QNetworkReply>
namespace OCC {
Q_LOGGING_CATEGORY(lcUpdateFileDropMetadataJob, "nextcloud.sync.propagator.updatefiledropmetadatajob", QtInfoMsg)
}
namespace OCC {
UpdateFileDropMetadataJob::UpdateFileDropMetadataJob(OwncloudPropagator *propagator, const QString &path)
: PropagatorJob(propagator)
, _path(path)
{
}
void UpdateFileDropMetadataJob::start()
{
qCDebug(lcUpdateFileDropMetadataJob) << "Folder is encrypted, let's get the Id from it.";
const auto fetchFolderEncryptedIdJob = new LsColJob(propagator()->account(), _path, this);
fetchFolderEncryptedIdJob->setProperties({"resourcetype", "http://owncloud.org/ns:fileid"});
connect(fetchFolderEncryptedIdJob, &LsColJob::directoryListingSubfolders, this, &UpdateFileDropMetadataJob::slotFolderEncryptedIdReceived);
connect(fetchFolderEncryptedIdJob, &LsColJob::finishedWithError, this, &UpdateFileDropMetadataJob::slotFolderEncryptedIdError);
fetchFolderEncryptedIdJob->start();
}
bool UpdateFileDropMetadataJob::scheduleSelfOrChild()
{
if (_state == Finished) {
return false;
}
if (_state == NotYetStarted) {
_state = Running;
start();
}
return true;
}
PropagatorJob::JobParallelism UpdateFileDropMetadataJob::parallelism() const
{
return PropagatorJob::JobParallelism::WaitForFinished;
}
void UpdateFileDropMetadataJob::slotFolderEncryptedIdReceived(const QStringList &list)
{
qCDebug(lcUpdateFileDropMetadataJob) << "Received id of folder, trying to lock it so we can prepare the metadata";
const auto fetchFolderEncryptedIdJob = qobject_cast<LsColJob *>(sender());
Q_ASSERT(fetchFolderEncryptedIdJob);
if (!fetchFolderEncryptedIdJob) {
qCCritical(lcUpdateFileDropMetadataJob) << "slotFolderEncryptedIdReceived must be called by a signal";
emit finished(SyncFileItem::Status::FatalError);
return;
}
Q_ASSERT(!list.isEmpty());
if (list.isEmpty()) {
qCCritical(lcUpdateFileDropMetadataJob) << "slotFolderEncryptedIdReceived list.isEmpty()";
emit finished(SyncFileItem::Status::FatalError);
return;
}
const auto &folderInfo = fetchFolderEncryptedIdJob->_folderInfos.value(list.first());
slotTryLock(folderInfo.fileId);
}
void UpdateFileDropMetadataJob::slotTryLock(const QByteArray &fileId)
{
const auto lockJob = new LockEncryptFolderApiJob(propagator()->account(), fileId, propagator()->_journal, propagator()->account()->e2e()->_publicKey, this);
connect(lockJob, &LockEncryptFolderApiJob::success, this, &UpdateFileDropMetadataJob::slotFolderLockedSuccessfully);
connect(lockJob, &LockEncryptFolderApiJob::error, this, &UpdateFileDropMetadataJob::slotFolderLockedError);
lockJob->start();
}
void UpdateFileDropMetadataJob::slotFolderLockedSuccessfully(const QByteArray &fileId, const QByteArray &token)
{
qCDebug(lcUpdateFileDropMetadataJob) << "Folder" << fileId << "Locked Successfully for Upload, Fetching Metadata";
_folderToken = token;
_folderId = fileId;
_isFolderLocked = true;
const auto fetchMetadataJob = new GetMetadataApiJob(propagator()->account(), _folderId);
connect(fetchMetadataJob, &GetMetadataApiJob::jsonReceived, this, &UpdateFileDropMetadataJob::slotFolderEncryptedMetadataReceived);
connect(fetchMetadataJob, &GetMetadataApiJob::error, this, &UpdateFileDropMetadataJob::slotFolderEncryptedMetadataError);
fetchMetadataJob->start();
}
void UpdateFileDropMetadataJob::slotFolderEncryptedMetadataError(const QByteArray &fileId, int httpReturnCode)
{
Q_UNUSED(fileId);
Q_UNUSED(httpReturnCode);
qCDebug(lcUpdateFileDropMetadataJob()) << "Error Getting the encrypted metadata. Pretend we got empty metadata.";
const FolderMetadata emptyMetadata(propagator()->account());
const auto encryptedMetadataJson = QJsonDocument::fromJson(emptyMetadata.encryptedMetadata());
slotFolderEncryptedMetadataReceived(encryptedMetadataJson, httpReturnCode);
}
void UpdateFileDropMetadataJob::slotFolderEncryptedMetadataReceived(const QJsonDocument &json, int statusCode)
{
qCDebug(lcUpdateFileDropMetadataJob) << "Metadata Received, Preparing it for the new file." << json.toVariant();
// Encrypt File!
_metadata.reset(new FolderMetadata(propagator()->account(),
FolderMetadata::RequiredMetadataVersion::Version1,
json.toJson(QJsonDocument::Compact), statusCode));
if (!_metadata->moveFromFileDropToFiles() && !_metadata->encryptedMetadataNeedUpdate()) {
unlockFolder();
return;
}
emit fileDropMetadataParsedAndAdjusted(_metadata.data());
const auto updateMetadataJob = new UpdateMetadataApiJob(propagator()->account(), _folderId, _metadata->encryptedMetadata(), _folderToken);
connect(updateMetadataJob, &UpdateMetadataApiJob::success, this, &UpdateFileDropMetadataJob::slotUpdateMetadataSuccess);
connect(updateMetadataJob, &UpdateMetadataApiJob::error, this, &UpdateFileDropMetadataJob::slotUpdateMetadataError);
updateMetadataJob->start();
}
void UpdateFileDropMetadataJob::slotUpdateMetadataSuccess(const QByteArray &fileId)
{
Q_UNUSED(fileId);
qCDebug(lcUpdateFileDropMetadataJob) << "Uploading of the metadata success, Encrypting the file";
qCDebug(lcUpdateFileDropMetadataJob) << "Finalizing the upload part, now the actual uploader will take over";
unlockFolder();
}
void UpdateFileDropMetadataJob::slotUpdateMetadataError(const QByteArray &fileId, int httpErrorResponse)
{
qCDebug(lcUpdateFileDropMetadataJob) << "Update metadata error for folder" << fileId << "with error" << httpErrorResponse;
qCDebug(lcUpdateFileDropMetadataJob()) << "Unlocking the folder.";
unlockFolder();
}
void UpdateFileDropMetadataJob::slotFolderLockedError(const QByteArray &fileId, int httpErrorCode)
{
Q_UNUSED(httpErrorCode);
qCDebug(lcUpdateFileDropMetadataJob) << "Folder" << fileId << "with path" << _path << "Coundn't be locked. httpErrorCode" << httpErrorCode;
emit finished(SyncFileItem::Status::NormalError);
}
void UpdateFileDropMetadataJob::slotFolderEncryptedIdError(QNetworkReply *reply)
{
if (!reply) {
qCDebug(lcUpdateFileDropMetadataJob) << "Error retrieving the Id of the encrypted folder" << _path;
} else {
qCDebug(lcUpdateFileDropMetadataJob) << "Error retrieving the Id of the encrypted folder" << _path << "with httpErrorCode" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
}
emit finished(SyncFileItem::Status::NormalError);
}
void UpdateFileDropMetadataJob::unlockFolder()
{
Q_ASSERT(!_isUnlockRunning);
if (!_isFolderLocked) {
emit finished(SyncFileItem::Status::Success);
return;
}
if (_isUnlockRunning) {
qCWarning(lcUpdateFileDropMetadataJob) << "Double-call to unlockFolder.";
return;
}
_isUnlockRunning = true;
qCDebug(lcUpdateFileDropMetadataJob) << "Calling Unlock";
const auto unlockJob = new UnlockEncryptFolderApiJob(propagator()->account(), _folderId, _folderToken, propagator()->_journal, this);
connect(unlockJob, &UnlockEncryptFolderApiJob::success, [this](const QByteArray &folderId) {
qCDebug(lcUpdateFileDropMetadataJob) << "Successfully Unlocked";
_folderToken = "";
_folderId = "";
_isFolderLocked = false;
emit folderUnlocked(folderId, 200);
_isUnlockRunning = false;
emit finished(SyncFileItem::Status::Success);
});
connect(unlockJob, &UnlockEncryptFolderApiJob::error, [this](const QByteArray &folderId, int httpStatus) {
qCDebug(lcUpdateFileDropMetadataJob) << "Unlock Error";
emit folderUnlocked(folderId, httpStatus);
_isUnlockRunning = false;
emit finished(SyncFileItem::Status::NormalError);
});
unlockJob->start();
}
}