Merge pull request #5263 from nextcloud/feature/encrypt-folder-context-menu

Add an "Encrypt" menu entry in file browser context menu for folders
This commit is contained in:
Claudio Cambra 2022-12-13 14:31:00 +01:00 committed by GitHub
commit d7f9ad6a07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 143 additions and 16 deletions

View file

@ -26,6 +26,7 @@
#include "deletejob.h"
#include "folderman.h"
#include "folder.h"
#include "encryptfolderjob.h"
#include "theme.h"
#include "common/syncjournalfilerecord.h"
#include "syncengine.h"
@ -82,6 +83,11 @@
// The second number should be changed when there are new features.
#define MIRALL_SOCKET_API_VERSION "1.1"
namespace {
constexpr auto encryptJobPropertyFolder = "folder";
constexpr auto encryptJobPropertyPath = "path";
}
namespace {
const QLatin1Char RecordSeparator()
@ -501,6 +507,61 @@ void SocketApi::processFileActivityRequest(const QString &localFile)
emit fileActivityCommandReceived(fileData.localPath);
}
void SocketApi::processEncryptRequest(const QString &localFile)
{
Q_ASSERT(QFileInfo(localFile).isDir());
const auto fileData = FileData::get(localFile);
const auto folder = fileData.folder;
Q_ASSERT(folder);
const auto account = folder->accountState()->account();
Q_ASSERT(account);
const auto rec = fileData.journalRecord();
Q_ASSERT(rec.isValid());
if (!account->e2e() || account->e2e()->_mnemonic.isEmpty()) {
const int ret = QMessageBox::critical(nullptr,
tr("Failed to encrypt folder at \"%1\"").arg(fileData.folderRelativePath),
tr("The account %1 does not have end-to-end encryption configured. "
"Please configure this in your account settings to enable folder encryption.").arg(account->prettyName()));
Q_UNUSED(ret)
return;
}
auto path = rec._path;
// Folder records have directory paths in Foo/Bar/ convention...
// But EncryptFolderJob expects directory path Foo/Bar convention
auto choppedPath = path;
if (choppedPath.endsWith('/') && choppedPath != QStringLiteral("/")) {
choppedPath.chop(1);
}
if (choppedPath.startsWith('/') && choppedPath != QStringLiteral("/")) {
choppedPath = choppedPath.mid(1);
}
auto job = new OCC::EncryptFolderJob(account, folder->journalDb(), choppedPath, rec.numericFileId(), this);
connect(job, &OCC::EncryptFolderJob::finished, this, [fileData, job](const int status) {
if (status == OCC::EncryptFolderJob::Error) {
const int ret = QMessageBox::critical(nullptr,
tr("Failed to encrypt folder"),
tr("Could not encrypt the following folder: \"%1\". \n\n"
"Server replied with error: %2").arg(fileData.folderRelativePath, job->errorString()));
Q_UNUSED(ret)
} else {
const int ret = QMessageBox::information(nullptr,
tr("Folder encrypted successfully").arg(fileData.folderRelativePath),
tr("The following folder was encrypted successfully: \"%1\"").arg(fileData.folderRelativePath));
Q_UNUSED(ret)
}
});
job->setProperty(encryptJobPropertyFolder, QVariant::fromValue(folder));
job->setProperty(encryptJobPropertyPath, QVariant::fromValue(path));
job->start();
}
void SocketApi::processShareRequest(const QString &localFile, SocketListener *listener)
{
auto theme = Theme::instance();
@ -603,6 +664,13 @@ void SocketApi::command_ACTIVITY(const QString &localFile, SocketListener *liste
processFileActivityRequest(localFile);
}
void SocketApi::command_ENCRYPT(const QString &localFile, SocketListener *listener)
{
Q_UNUSED(listener);
processEncryptRequest(localFile);
}
void SocketApi::command_MANAGE_PUBLIC_LINKS(const QString &localFile, SocketListener *listener)
{
processShareRequest(localFile, listener);
@ -1093,6 +1161,39 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi
//listener->sendMessage(QLatin1String("MENU_ITEM:EMAIL_PRIVATE_LINK") + flagString + tr("Send private link by email …"));
}
void SocketApi::sendEncryptFolderCommandMenuEntries(const QFileInfo &fileInfo,
const FileData &fileData,
const bool isE2eEncryptedPath,
const OCC::SocketListener* const listener) const
{
if (!listener ||
!fileData.folder ||
!fileData.folder->accountState() ||
!fileData.folder->accountState()->account() ||
!fileData.folder->accountState()->account()->capabilities().clientSideEncryptionAvailable() ||
!fileInfo.isDir() ||
isE2eEncryptedPath) {
return;
}
bool anyAncestorEncrypted = false;
auto ancestor = fileData.parentFolder();
while (ancestor.journalRecord().isValid()) {
if (ancestor.journalRecord()._isE2eEncrypted) {
anyAncestorEncrypted = true;
break;
}
ancestor = ancestor.parentFolder();
}
if (!anyAncestorEncrypted) {
const auto isOnTheServer = fileData.journalRecord().isValid();
const auto flagString = isOnTheServer ? QLatin1String("::") : QLatin1String(":d:");
listener->sendMessage(QStringLiteral("MENU_ITEM:ENCRYPT") + flagString + tr("Encrypt"));
}
}
void SocketApi::sendLockFileCommandMenuEntries(const QFileInfo &fileInfo,
Folder* const syncFolder,
const FileData &fileData,
@ -1215,6 +1316,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
const QFileInfo fileInfo(fileData.localPath);
sendLockFileInfoMenuEntries(fileInfo, syncFolder, fileData, listener, record);
if (!fileInfo.isDir()) {
listener->sendMessage(QLatin1String("MENU_ITEM:ACTIVITY") + flagString + tr("Activity"));
}
@ -1227,6 +1329,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
listener->sendMessage(QLatin1String("MENU_ITEM:OPEN_PRIVATE_LINK") + flagString + tr("Open in browser"));
}
sendEncryptFolderCommandMenuEntries(fileInfo, fileData, isE2eEncryptedPath, listener);
sendLockFileCommandMenuEntries(fileInfo, syncFolder, fileData, listener);
sendSharingContextMenuOptions(fileData, listener, !isE2eEncryptedPath);

View file

@ -104,6 +104,7 @@ private:
void processShareRequest(const QString &localFile, SocketListener *listener);
void processLeaveShareRequest(const QString &localFile, SocketListener *listener);
void processFileActivityRequest(const QString &localFile);
void processEncryptRequest(const QString &localFile);
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString &argument, OCC::SocketListener *listener);
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString &argument, OCC::SocketListener *listener);
@ -114,6 +115,7 @@ private:
// The context menu actions
Q_INVOKABLE void command_ACTIVITY(const QString &localFile, OCC::SocketListener *listener);
Q_INVOKABLE void command_ENCRYPT(const QString &localFile, SocketListener *listener);
Q_INVOKABLE void command_SHARE(const QString &localFile, OCC::SocketListener *listener);
Q_INVOKABLE void command_LEAVESHARE(const QString &localFile, SocketListener *listener);
Q_INVOKABLE void command_MANAGE_PUBLIC_LINKS(const QString &localFile, OCC::SocketListener *listener);
@ -151,8 +153,15 @@ private:
// Sends the context menu options relating to sharing to listener
void sendSharingContextMenuOptions(const FileData &fileData, SocketListener *listener, bool enabled);
void
sendLockFileCommandMenuEntries(const QFileInfo &fileInfo, Folder *const syncFolder, const FileData &fileData, const SocketListener *const listener) const;
void sendEncryptFolderCommandMenuEntries(const QFileInfo &fileInfo,
const FileData &fileData,
const bool isE2eEncryptedPath,
const OCC::SocketListener* const listener) const;
void sendLockFileCommandMenuEntries(const QFileInfo &fileInfo,
Folder *const syncFolder,
const FileData &fileData,
const SocketListener *const listener) const;
void sendLockFileInfoMenuEntries(const QFileInfo &fileInfo,
Folder* const syncFolder,

View file

@ -177,7 +177,7 @@ bool UnlockEncryptFolderApiJob::finished()
if (retCode != 200) {
qCInfo(lcCseJob()) << "error unlocking file" << path() << errorString() << retCode;
qCInfo(lcCseJob()) << "Full Error Log" << reply()->readAll();
emit error(_fileId, retCode);
emit error(_fileId, retCode, errorString());
return true;
}
emit success(_fileId);
@ -241,7 +241,7 @@ bool LockEncryptFolderApiJob::finished()
int retCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (retCode != 200) {
qCInfo(lcCseJob()) << "error locking file" << path() << errorString() << retCode;
emit error(_fileId, retCode);
emit error(_fileId, retCode, errorString());
return true;
}
@ -282,7 +282,7 @@ bool SetEncryptionFlagApiJob::finished()
emit success(_fileId);
} else {
qCInfo(lcCseJob()) << "Setting the encrypted flag failed with" << path() << errorString() << retCode;
emit error(_fileId, retCode);
emit error(_fileId, retCode, errorString());
}
return true;
}

View file

@ -129,7 +129,9 @@ protected:
signals:
void success(const QByteArray &fileId);
void error(const QByteArray &fileId, int httpReturnCode);
void error(const QByteArray &fileId,
const int httpReturnCode,
const QString &errorMessage);
private:
QByteArray _fileId;
@ -150,7 +152,9 @@ protected:
signals:
void success(const QByteArray& fileId, const QByteArray& token);
void error(const QByteArray& fileId, int httpdErrorCode);
void error(const QByteArray& fileId,
const int httpErrorCode,
const QString &errorMessage);
private:
QByteArray _fileId;
@ -175,7 +179,9 @@ protected:
signals:
void success(const QByteArray& fileId);
void error(const QByteArray& fileId, int httpReturnCode);
void error(const QByteArray& fileId,
const int httpReturnCode,
const QString &errorMessage);
private:
QByteArray _fileId;

View file

@ -70,9 +70,12 @@ void EncryptFolderJob::slotEncryptionFlagSuccess(const QByteArray &fileId)
lockJob->start();
}
void EncryptFolderJob::slotEncryptionFlagError(const QByteArray &fileId, int httpErrorCode)
void EncryptFolderJob::slotEncryptionFlagError(const QByteArray &fileId,
const int httpErrorCode,
const QString &errorMessage)
{
qDebug() << "Error on the encryption flag of" << fileId << "HTTP code:" << httpErrorCode;
_errorString = errorMessage;
emit finished(Error);
}
@ -108,7 +111,7 @@ void EncryptFolderJob::slotUploadMetadataSuccess(const QByteArray &folderId)
unlockJob->start();
}
void EncryptFolderJob::slotUpdateMetadataError(const QByteArray &folderId, int httpReturnCode)
void EncryptFolderJob::slotUpdateMetadataError(const QByteArray &folderId, const int httpReturnCode)
{
Q_UNUSED(httpReturnCode);
@ -120,15 +123,21 @@ void EncryptFolderJob::slotUpdateMetadataError(const QByteArray &folderId, int h
unlockJob->start();
}
void EncryptFolderJob::slotLockForEncryptionError(const QByteArray &fileId, int httpErrorCode)
void EncryptFolderJob::slotLockForEncryptionError(const QByteArray &fileId,
const int httpErrorCode,
const QString &errorMessage)
{
qCInfo(lcEncryptFolderJob()) << "Locking error for" << fileId << "HTTP code:" << httpErrorCode;
_errorString = errorMessage;
emit finished(Error);
}
void EncryptFolderJob::slotUnlockFolderError(const QByteArray &fileId, int httpErrorCode)
void EncryptFolderJob::slotUnlockFolderError(const QByteArray &fileId,
const int httpErrorCode,
const QString &errorMessage)
{
qCInfo(lcEncryptFolderJob()) << "Unlocking error for" << fileId << "HTTP code:" << httpErrorCode;
_errorString = errorMessage;
emit finished(Error);
}
void EncryptFolderJob::slotUnlockFolderSuccess(const QByteArray &fileId)

View file

@ -40,13 +40,13 @@ signals:
private slots:
void slotEncryptionFlagSuccess(const QByteArray &folderId);
void slotEncryptionFlagError(const QByteArray &folderId, int httpReturnCode);
void slotEncryptionFlagError(const QByteArray &folderId, const int httpReturnCode, const QString &errorMessage);
void slotLockForEncryptionSuccess(const QByteArray &folderId, const QByteArray &token);
void slotLockForEncryptionError(const QByteArray &folderId, int httpReturnCode);
void slotLockForEncryptionError(const QByteArray &folderId, const int httpReturnCode, const QString &errorMessage);
void slotUnlockFolderSuccess(const QByteArray &folderId);
void slotUnlockFolderError(const QByteArray &folderId, int httpReturnCode);
void slotUnlockFolderError(const QByteArray &folderId, const int httpReturnCode, const QString &errorMessage);
void slotUploadMetadataSuccess(const QByteArray &folderId);
void slotUpdateMetadataError(const QByteArray &folderId, int httpReturnCode);
void slotUpdateMetadataError(const QByteArray &folderId, const int httpReturnCode);
private:
AccountPtr _account;