/* * Copyright (C) by Oleksandr Zolotov * * 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 #include #include "foldermetadata.h" #include "basepropagateremotedeleteencrypted.h" #include "account.h" #include "clientsideencryptionjobs.h" #include "deletejob.h" #include "owncloudpropagator.h" Q_LOGGING_CATEGORY(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED, "nextcloud.sync.propagator.remove.encrypted") namespace OCC { BasePropagateRemoteDeleteEncrypted::BasePropagateRemoteDeleteEncrypted(OwncloudPropagator *propagator, SyncFileItemPtr item, QObject *parent) : QObject(parent) , _propagator(propagator) , _item(item) {} QNetworkReply::NetworkError BasePropagateRemoteDeleteEncrypted::networkError() const { return _networkError; } QString BasePropagateRemoteDeleteEncrypted::errorString() const { return _errorString; } void BasePropagateRemoteDeleteEncrypted::storeFirstError(QNetworkReply::NetworkError err) { if (_networkError == QNetworkReply::NetworkError::NoError) { _networkError = err; } } void BasePropagateRemoteDeleteEncrypted::storeFirstErrorString(const QString &errString) { if (_errorString.isEmpty()) { _errorString = errString; } } void BasePropagateRemoteDeleteEncrypted::fetchMetadataForPath(const QString &path) { qCDebug(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Folder is encrypted, let's fetch its metadata."; SyncJournalFileRecord rec; if (!_propagator->_journal->getRootE2eFolderRecord(Utility::noLeadingSlashPath(path), &rec) || !rec.isValid()) { taskFailed(); return; } _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_propagator->account(), _propagator->fullRemotePath(path), _propagator->remotePath(), _propagator->_journal, rec.path())); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::fetchFinished, this, &BasePropagateRemoteDeleteEncrypted::slotFetchMetadataJobFinished); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::uploadFinished, this, &BasePropagateRemoteDeleteEncrypted::slotUpdateMetadataJobFinished); _encryptedFolderMetadataHandler->fetchMetadata(); } void BasePropagateRemoteDeleteEncrypted::uploadMetadata(const EncryptedFolderMetadataHandler::UploadMode uploadMode) { _encryptedFolderMetadataHandler->uploadMetadata(uploadMode); } void BasePropagateRemoteDeleteEncrypted::slotFolderUnLockFinished(const QByteArray &folderId, int statusCode) { if (statusCode != 200) { _item->_httpErrorCode = statusCode; _errorString = tr("\"%1 Failed to unlock encrypted folder %2\".").arg(statusCode).arg(QString::fromUtf8(folderId)); _item->_errorString = _errorString; taskFailed(); return; } qCDebug(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Folder id" << folderId << "successfully unlocked"; } void BasePropagateRemoteDeleteEncrypted::slotDeleteRemoteItemFinished() { auto *deleteJob = qobject_cast(QObject::sender()); Q_ASSERT(deleteJob); if (!deleteJob) { qCCritical(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Sender is not a DeleteJob instance."; taskFailed(); return; } const auto err = deleteJob->reply()->error(); _item->_httpErrorCode = deleteJob->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); _item->_responseTimeStamp = deleteJob->responseTimestamp(); _item->_requestId = deleteJob->requestId(); if (err != QNetworkReply::NoError && err != QNetworkReply::ContentNotFoundError) { storeFirstErrorString(deleteJob->errorString()); storeFirstError(err); taskFailed(); return; } // A 404 reply is also considered a success here: We want to make sure // a file is gone from the server. It not being there in the first place // is ok. This will happen for files that are in the DB but not on // the server or the local file system. if (_item->_httpErrorCode != 204 && _item->_httpErrorCode != 404) { // Normally we expect "204 No Content" // If it is not the case, it might be because of a proxy or gateway intercepting the request, so we must // throw an error. storeFirstErrorString(tr("Wrong HTTP code returned by server. Expected 204, but received \"%1 %2\".") .arg(_item->_httpErrorCode) .arg(deleteJob->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString())); taskFailed(); return; } if (!_propagator->_journal->deleteFileRecord(_item->_originalFile, _item->isDirectory())) { qCWarning(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Failed to delete file record from local DB" << _item->_originalFile; } _propagator->_journal->commit("Remote Remove"); unlockFolder(EncryptedFolderMetadataHandler::UnlockFolderWithResult::Success); } void BasePropagateRemoteDeleteEncrypted::deleteRemoteItem(const QString &filename) { qCInfo(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Deleting nested encrypted item" << filename; const auto deleteJob = new DeleteJob(_propagator->account(), _propagator->fullRemotePath(filename), this); if (_encryptedFolderMetadataHandler && _encryptedFolderMetadataHandler->folderMetadata() && _encryptedFolderMetadataHandler->folderMetadata()->isValid()) { deleteJob->setFolderToken(_encryptedFolderMetadataHandler->folderToken()); } connect(deleteJob, &DeleteJob::finishedSignal, this, &BasePropagateRemoteDeleteEncrypted::slotDeleteRemoteItemFinished); deleteJob->start(); } void BasePropagateRemoteDeleteEncrypted::unlockFolder(const EncryptedFolderMetadataHandler::UnlockFolderWithResult result) { if (!_encryptedFolderMetadataHandler) { qCWarning(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Null _encryptedFolderMetadataHandler"; } if (!_encryptedFolderMetadataHandler || !_encryptedFolderMetadataHandler->isFolderLocked()) { emit finished(true); return; } qCDebug(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Unlocking folder" << _encryptedFolderMetadataHandler->folderId(); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::folderUnlocked, this, &BasePropagateRemoteDeleteEncrypted::slotFolderUnLockFinished); _encryptedFolderMetadataHandler->unlockFolder(result); } void BasePropagateRemoteDeleteEncrypted::taskFailed() { qCDebug(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Task failed for job" << sender(); _isTaskFailed = true; if (_encryptedFolderMetadataHandler && _encryptedFolderMetadataHandler->isFolderLocked()) { unlockFolder(EncryptedFolderMetadataHandler::UnlockFolderWithResult::Failure); } else { emit finished(false); } } QSharedPointer BasePropagateRemoteDeleteEncrypted::folderMetadata() const { Q_ASSERT(_encryptedFolderMetadataHandler->folderMetadata()); if (!_encryptedFolderMetadataHandler->folderMetadata()) { qCWarning(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Metadata is null!"; } return _encryptedFolderMetadataHandler->folderMetadata(); } const QByteArray BasePropagateRemoteDeleteEncrypted::folderToken() const { return _encryptedFolderMetadataHandler->folderToken(); } } // namespace OCC