mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-25 14:36:01 +03:00
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:
commit
d7f9ad6a07
6 changed files with 143 additions and 16 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue