#include "propagatedownloadencrypted.h" #include "clientsideencryptionjobs.h" Q_LOGGING_CATEGORY(lcPropagateDownloadEncrypted, "nextcloud.sync.propagator.download.encrypted", QtInfoMsg) namespace OCC { PropagateDownloadEncrypted::PropagateDownloadEncrypted(OwncloudPropagator *propagator, const QString &localParentPath, SyncFileItemPtr item, QObject *parent) : QObject(parent) , _propagator(propagator) , _localParentPath(localParentPath) , _item(item) , _info(_item->_file) { } void PropagateDownloadEncrypted::start() { checkFolderEncryptedStatus(); } void PropagateDownloadEncrypted::checkFolderEncryptedStatus() { const auto rootPath = [=]() { const auto result = _propagator->_remoteFolder; if (result.startsWith('/')) { return result.mid(1); } else { return result; } }(); const auto remoteFilename = _item->_encryptedFileName.isEmpty() ? _item->_file : _item->_encryptedFileName; const auto remotePath = QString(rootPath + remoteFilename); const auto remoteParentPath = remotePath.left(remotePath.lastIndexOf('/')); auto getEncryptedStatus = new GetFolderEncryptStatusJob(_propagator->account(), remoteParentPath, this); connect(getEncryptedStatus, &GetFolderEncryptStatusJob::encryptStatusFolderReceived, this, &PropagateDownloadEncrypted::folderStatusReceived); connect(getEncryptedStatus, &GetFolderEncryptStatusJob::encryptStatusError, this, &PropagateDownloadEncrypted::folderStatusError); getEncryptedStatus->start(); } void PropagateDownloadEncrypted::folderStatusError(int statusCode) { qCDebug(lcPropagateDownloadEncrypted) << "Failed to get encrypted status of folder" << statusCode; } void PropagateDownloadEncrypted::folderStatusReceived(const QString &folder, bool isEncrypted) { qCDebug(lcPropagateDownloadEncrypted) << "Get Folder is Encrypted Received" << folder << isEncrypted; if (!isEncrypted) { emit folderStatusNotEncrypted(); return; } // Is encrypted Now we need the folder-id auto job = new LsColJob(_propagator->account(), folder, this); job->setProperties({"resourcetype", "http://owncloud.org/ns:fileid"}); connect(job, &LsColJob::directoryListingSubfolders, this, &PropagateDownloadEncrypted::checkFolderId); connect(job, &LsColJob::finishedWithError, this, &PropagateDownloadEncrypted::folderIdError); job->start(); } void PropagateDownloadEncrypted::folderIdError() { qCDebug(lcPropagateDownloadEncrypted) << "Failed to get encrypted metadata of folder"; } void PropagateDownloadEncrypted::checkFolderId(const QStringList &list) { auto job = qobject_cast(sender()); const QString folderId = list.first(); qCDebug(lcPropagateDownloadEncrypted) << "Received id of folder" << folderId; const ExtraFolderInfo &folderInfo = job->_folderInfos.value(folderId); // Now that we have the folder-id we need it's JSON metadata auto metadataJob = new GetMetadataApiJob(_propagator->account(), folderInfo.fileId); connect(metadataJob, &GetMetadataApiJob::jsonReceived, this, &PropagateDownloadEncrypted::checkFolderEncryptedMetadata); metadataJob->start(); } void PropagateDownloadEncrypted::checkFolderEncryptedMetadata(const QJsonDocument &json) { qCDebug(lcPropagateDownloadEncrypted) << "Metadata Received reading" << csync_instruction_str(_item->_instruction) << _item->_file << _item->_encryptedFileName; const QString filename = _info.fileName(); auto meta = new FolderMetadata(_propagator->account(), json.toJson(QJsonDocument::Compact)); const QVector files = meta->files(); const QString encryptedFilename = _item->_instruction == CSYNC_INSTRUCTION_NEW ? _item->_file.section(QLatin1Char('/'), -1) : _item->_encryptedFileName.section(QLatin1Char('/'), -1); for (const EncryptedFile &file : files) { if (encryptedFilename == file.encryptedFilename) { _encryptedInfo = file; if (_item->_encryptedFileName.isEmpty()) { _item->_encryptedFileName = _item->_file; } if (!_localParentPath.isEmpty()) { _item->_file = _localParentPath + QLatin1Char('/') + _encryptedInfo.originalFilename; } else { _item->_file = _encryptedInfo.originalFilename; } qCDebug(lcPropagateDownloadEncrypted) << "Found matching encrypted metadata for file, starting download"; emit folderStatusEncrypted(); return; } } emit failed(); qCCritical(lcPropagateDownloadEncrypted) << "Failed to find encrypted metadata information of remote file" << filename; } // TODO: Fix this. Exported in the wrong place. QString createDownloadTmpFileName(const QString &previous); bool PropagateDownloadEncrypted::decryptFile(QFile& tmpFile) { const QString tmpFileName = createDownloadTmpFileName(_item->_file + QLatin1String("_dec")); qCDebug(lcPropagateDownloadEncrypted) << "Content Checksum Computed starting decryption" << tmpFileName; tmpFile.close(); QFile _tmpOutput(_propagator->getFilePath(tmpFileName), this); EncryptionHelper::fileDecryption(_encryptedInfo.encryptionKey, _encryptedInfo.initializationVector, &tmpFile, &_tmpOutput); qCDebug(lcPropagateDownloadEncrypted) << "Decryption finished" << tmpFile.fileName() << _tmpOutput.fileName(); tmpFile.close(); _tmpOutput.close(); // we decripted the temporary into another temporary, so good bye old one if (!tmpFile.remove()) { qCDebug(lcPropagateDownloadEncrypted) << "Failed to remove temporary file" << tmpFile.errorString(); _errorString = tmpFile.errorString(); return false; } // Let's fool the rest of the logic into thinking this was the actual download tmpFile.setFileName(_tmpOutput.fileName()); return true; } QString PropagateDownloadEncrypted::errorString() const { return _errorString; } }