Skip to content

Commit 9ae6539

Browse files
committed
Fix re-encrypting tabs loaded with other plugins
1 parent 9af4933 commit 9ae6539

File tree

10 files changed

+133
-51
lines changed

10 files changed

+133
-51
lines changed

src/gui/encryptionpassword.cpp

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "item/itemstore.h"
1515
#include "item/itemwidget.h"
1616
#include "item/serialize.h"
17+
#include "gui/clipboardbrowsershared.h"
1718

1819
#include <QCoreApplication>
1920
#include <QInputDialog>
@@ -325,9 +326,9 @@ Encryption::EncryptionKey promptForEncryptionPasswordChange(QWidget *parent)
325326

326327
bool reencryptTabs(
327328
const QStringList &tabNames,
328-
ItemFactory *itemFactory,
329+
ClipboardBrowserShared *sharedData,
329330
const Encryption::EncryptionKey &oldKey,
330-
Encryption::EncryptionKey &newKey,
331+
const Encryption::EncryptionKey &newKey,
331332
int maxItems,
332333
QWidget *parent)
333334
{
@@ -342,13 +343,20 @@ bool reencryptTabs(
342343
progress.setMinimumDuration(500); // Show after 500ms if not done
343344
progress.setValue(0);
344345

345-
const Encryption::EncryptionKey newKeyBackup = newKey;
346+
// Skip plugins that do not support encryption
347+
ItemLoaderList skipLoaders;
348+
for (auto &loader : sharedData->itemFactory->loaders()) {
349+
if (loader->isEnabled() && !loader->supportsEncryption()) {
350+
skipLoaders.append(loader);
351+
loader->setEnabled(false);
352+
}
353+
}
346354

347355
QStringList failedTabs;
356+
348357
for (int i = 0; i < tabNames.size(); ++i) {
349358
if (progress.wasCanceled()) {
350359
log("Tab re-encryption cancelled by user", LogWarning);
351-
failedTabs.append(QObject::tr("(Cancelled by user)"));
352360
break;
353361
}
354362

@@ -369,14 +377,12 @@ bool reencryptTabs(
369377
ClipboardModel model;
370378

371379
// Set old encryption key temporarily and load items
372-
newKey = oldKey;
373-
ItemSaverPtr saver = loadItems(tabName, model, itemFactory, maxItems);
374-
newKey = newKeyBackup;
380+
sharedData->encryptionKey = oldKey;
381+
ItemSaverPtr saver = loadItems(tabName, model, sharedData->itemFactory, maxItems);
382+
sharedData->encryptionKey = newKey;
375383

376384
if (!saver) {
377-
const QString error = QStringLiteral("Failed to load tab: %1").arg(tabName);
378-
log(error, LogError);
379-
failedTabs.append(tabName);
385+
COPYQ_LOG(QStringLiteral("Skipping encryption on unsupported tab: %1").arg(tabName));
380386
continue;
381387
}
382388

@@ -387,15 +393,17 @@ bool reencryptTabs(
387393
COPYQ_LOG(QStringLiteral("Loaded %1 items from tab: %2").arg(itemCount).arg(tabName));
388394

389395
if (!saveItems(tabName, model, saver)) {
390-
const QString error = QStringLiteral("Failed to save tab: %1").arg(tabName);
391-
log(error, LogError);
396+
log(QStringLiteral("Failed to re-encrypt tab: %1").arg(tabName), LogError);
392397
failedTabs.append(tabName);
393398
continue;
394399
}
395400

396401
COPYQ_LOG(QStringLiteral("Successfully re-encrypted tab: %1").arg(tabName));
397402
}
398403

404+
for (auto &loader : skipLoaders)
405+
loader->setEnabled(true);
406+
399407
progress.setValue(tabNames.size());
400408

401409
if (!failedTabs.isEmpty()) {
@@ -411,11 +419,7 @@ bool reencryptTabs(
411419
return false;
412420
}
413421

414-
log(QStringLiteral("Successfully re-encrypted all %1 tabs").arg(tabNames.size()), LogNote);
415-
416-
if (!newKey.isValid())
417-
removePasswordFromKeychain();
418-
422+
log("Successfully re-encrypted tabs");
419423
return true;
420424
}
421425

@@ -444,9 +448,9 @@ Encryption::EncryptionKey promptForEncryptionPasswordChange(QWidget *)
444448

445449
bool reencryptTabs(
446450
const QStringList &,
447-
ItemFactory *,
451+
ClipboardBrowserShared *,
452+
const Encryption::EncryptionKey &,
448453
const Encryption::EncryptionKey &,
449-
Encryption::EncryptionKey &,
450454
int,
451455
QWidget *)
452456
{

src/gui/encryptionpassword.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "common/encryption.h"
66

7+
class ClipboardBrowserShared;
78
class QWidget;
89
class QAbstractItemModel;
910
class QIODevice;
@@ -46,9 +47,9 @@ Encryption::EncryptionKey promptForEncryptionPasswordChange(QWidget *parent);
4647
*/
4748
bool reencryptTabs(
4849
const QStringList &tabNames,
49-
ItemFactory *itemFactory,
50+
ClipboardBrowserShared *sharedData,
5051
const Encryption::EncryptionKey &oldKey,
51-
Encryption::EncryptionKey &newKey,
52+
const Encryption::EncryptionKey &newKey,
5253
int maxItems,
5354
QWidget *parent
5455
);

src/gui/mainwindow.cpp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3718,11 +3718,23 @@ void MainWindow::promptForEncryptionPasswordIfNeeded(AppConfig *appConfig)
37183718
}
37193719

37203720
void MainWindow::reencryptTabsIfNeeded(const QStringList &tabNames, AppConfig *appConfig)
3721+
{
3722+
if (m_reencrypting)
3723+
return;
3724+
3725+
m_reencrypting = true;
3726+
reencryptTabsIfNeededHelper(tabNames, appConfig);
3727+
m_reencrypting = false;
3728+
}
3729+
3730+
void MainWindow::reencryptTabsIfNeededHelper(const QStringList &tabNames, AppConfig *appConfig)
37213731
{
37223732
const bool isEncrypted = appConfig->option<Config::tab_encryption_enabled>();
37233733
if (m_wasEncrypted == isEncrypted)
37243734
return;
37253735

3736+
const Encryption::EncryptionKey newEncryptionKey = m_sharedData->encryptionKey;
3737+
37263738
// Always ask for the previous password when re-encrypting.
37273739
Encryption::EncryptionKey oldEncryptionKey;
37283740
if (m_wasEncrypted) {
@@ -3731,33 +3743,35 @@ void MainWindow::reencryptTabsIfNeeded(const QStringList &tabNames, AppConfig *a
37313743
}
37323744

37333745
// Revert encryption option if password was not provided.
3734-
if (!oldEncryptionKey.isValid() && !m_sharedData->encryptionKey.isValid()) {
3746+
if (!oldEncryptionKey.isValid() && !newEncryptionKey.isValid()) {
37353747
appConfig->setOption(Config::tab_encryption_enabled::name(), m_wasEncrypted);
37363748
return;
37373749
}
37383750

3739-
m_wasEncrypted = isEncrypted;
3740-
3741-
const bool ok = reencryptTabs(
3751+
const bool allEncrypted = reencryptTabs(
37423752
tabNames,
3743-
m_sharedData->itemFactory,
3753+
m_sharedData.get(),
37443754
oldEncryptionKey,
3745-
m_sharedData->encryptionKey,
3755+
newEncryptionKey,
37463756
Config::maxItems,
37473757
this
37483758
);
37493759

3750-
if (!ok) {
3751-
// If saving some tabs failed, keep the keys and the encryption enabled
3752-
if ( oldEncryptionKey.isValid() ) {
3760+
m_wasEncrypted = isEncrypted;
3761+
m_sharedData->encryptionKey = newEncryptionKey;
3762+
3763+
// If saving some tabs failed, keep the keys and the encryption enabled,
3764+
// otherwise remove unneeded key files.
3765+
if (oldEncryptionKey.isValid()) {
3766+
if (allEncrypted) {
3767+
Encryption::removeEncryptionKeys();
3768+
if (appConfig->option<Config::use_key_store>())
3769+
removePasswordFromKeychain();
3770+
} else {
37533771
appConfig->setOption(Config::tab_encryption_enabled::name(), true);
3772+
m_wasEncrypted = true;
37543773
m_sharedData->encryptionKey = oldEncryptionKey;
37553774
}
3756-
return;
3757-
}
3758-
3759-
if ( !m_sharedData->encryptionKey.isValid() ) {
3760-
Encryption::removeEncryptionKeys();
37613775
}
37623776
}
37633777

src/gui/mainwindow.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@ class MainWindow final : public QMainWindow
658658

659659
void promptForEncryptionPasswordIfNeeded(AppConfig *appConfig);
660660
void reencryptTabsIfNeeded(const QStringList &tabNames, AppConfig *appConfig);
661+
void reencryptTabsIfNeededHelper(const QStringList &tabNames, AppConfig *appConfig);
661662

662663
/**
663664
* Update tab name in placeholder and configuration.
@@ -686,6 +687,7 @@ class MainWindow final : public QMainWindow
686687

687688
ClipboardBrowserSharedPtr m_sharedData;
688689
bool m_wasEncrypted = false;
690+
bool m_reencrypting = false;
689691

690692
QVector<Command> m_automaticCommands;
691693
QVector<Command> m_displayCommands;

src/item/itemfactory.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ class DummyLoader final : public ItemLoaderInterface
270270
return filter.matches(text) || filter.matches(accentsRemoved(text));
271271
}
272272

273+
bool supportsEncryption() const override { return true; }
274+
273275
private:
274276
int m_itemDataThreshold = -1;
275277
ClipboardBrowserSharedPtr m_sharedData;
@@ -437,11 +439,13 @@ void ItemFactory::setPluginPriority(const QStringList &pluginNames)
437439
ItemSaverPtr ItemFactory::loadItems(const QString &tabName, QAbstractItemModel *model, QIODevice *file, int maxItems)
438440
{
439441
for (auto &loader : m_loaders) {
440-
if ( !loader->isEnabled() )
441-
continue;
442-
443442
file->seek(0);
444443
if ( loader->canLoadItems(file) ) {
444+
if ( !loader->isEnabled() ) {
445+
log(QStringLiteral("Cannot load tab %1, plugin %2 must be enabled")
446+
.arg(quoteString(tabName), loader->name()));
447+
return nullptr;
448+
}
445449
file->seek(0);
446450
auto saver = loader->loadItems(tabName, model, file, maxItems);
447451
if (!saver)

src/item/itemstore.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ ItemSaverPtr loadItems(const QString &tabName, QAbstractItemModel &model, ItemFa
103103
return saver;
104104
}
105105

106-
log( QStringLiteral("Tab \"%1\": Failed to load tab file: %2")
107-
.arg(tabName, tabFileName), LogError );
106+
log( QStringLiteral("Tab \"%1\": Cannot load tab file: %2")
107+
.arg(tabName, tabFileName) );
108108
model.removeRows(0, model.rowCount());
109109
return nullptr;
110110
}

src/item/itemwidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,8 @@ class ItemLoaderInterface
382382
*/
383383
virtual QObject *createExternalEditor(const QModelIndex &index, const QVariantMap &data, QWidget *parent) const;
384384

385+
virtual bool supportsEncryption() const { return false; }
386+
385387
ItemLoaderInterface(const ItemLoaderInterface &) = delete;
386388
ItemLoaderInterface &operator=(const ItemLoaderInterface &) = delete;
387389

src/item/serialize.cpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,31 +128,30 @@ QDataStream &operator>>(QDataStream &in, DataFile &value)
128128

129129
bool hasKey = false;
130130
in >> hasKey;
131-
#ifdef WITH_QCA_ENCRYPTION
132131
if (hasKey) {
133-
QByteArray dekBytes;
134-
in >> dekBytes;
135-
136-
// Initialize QCA if not already done
132+
#ifdef WITH_QCA_ENCRYPTION
137133
if (!Encryption::initialize()) {
138134
qCCritical(serializeCategory) << "Failed to initialize encryption for DataFile deserialization";
139135
in.setStatus(QDataStream::ReadCorruptData);
140136
return in;
141137
}
142138

139+
QByteArray dekBytes;
140+
in >> dekBytes;
141+
143142
Encryption::EncryptionKey key;
144-
if (key.importDEK(dekBytes)) {
145-
value.setEncryptionKey(key);
146-
} else {
143+
if (!key.importDEK(dekBytes)) {
147144
qCCritical(serializeCategory) << "Failed to import encryption key for DataFile";
145+
in.setStatus(QDataStream::ReadCorruptData);
146+
return in;
148147
}
149-
}
148+
149+
value.setEncryptionKey(key);
150150
#else
151-
if (hasKey) {
152151
qCCritical(serializeCategory) << "Cannot deserialize encrypted DataFile: encryption support not compiled";
153152
in.setStatus(QDataStream::ReadCorruptData);
154-
}
155153
#endif
154+
}
156155
return in;
157156
}
158157

src/tests/itemsynctests.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,3 +1030,57 @@ void ItemSyncTests::copyFiles()
10301030
RUN("tab" << tab3 << getFirstItemFormats, "text/plain\n");
10311031
QCOMPARE(dir3.files().join(" "), "test-0001.txt test.txt");
10321032
}
1033+
1034+
void ItemSyncTests::encryptionShouldNotAffectFiles()
1035+
{
1036+
TestDir dir1(1);
1037+
const QString tab1 = testTab(1);
1038+
RUN(Args() << "show" << tab1, "");
1039+
1040+
const Args args = Args() << "tab" << tab1;
1041+
const QString inspect = R"(
1042+
const sel = ItemSelection().selectAll();
1043+
const items = sel.items();
1044+
for (const i in items) {
1045+
if (i > 0) { print(' ;; '); }
1046+
print(i + ':');
1047+
const item = items[i];
1048+
for (const format in item) {
1049+
if (!format.includes('itemsync') || format.includes('itemsync-basename')) {
1050+
const fmt = format.replace('application/x-copyq-', '');
1051+
print(' ' + fmt + ':' + item[format]);
1052+
}
1053+
}
1054+
}
1055+
)";
1056+
1057+
const QString file1 = "test1.txt";
1058+
TEST(createFile(dir1, file1, "TEXT1"));
1059+
QCOMPARE(dir1.files().join(sep), file1);
1060+
1061+
WAIT_ON_OUTPUT(args << "size", "1\n");
1062+
RUN(args << inspect, "0: itemsync-basename:test1 text/plain:TEXT1");
1063+
1064+
const QString file2_yyy = "test2.yyy";
1065+
const QString file2_txt = "test2.txt";
1066+
TEST(createFile(dir1, file2_yyy, "YYY"));
1067+
TEST(createFile(dir1, file2_txt, "TEXT2"));
1068+
1069+
WAIT_ON_OUTPUT(
1070+
args << inspect, "0: itemsync-basename:test1 text/plain:TEXT1 ;; 1: itemsync-basename:test2 test-zzz:YYY text/plain:TEXT2");
1071+
1072+
const QStringList files({file1, file2_txt, file2_yyy});
1073+
QCOMPARE(dir1.files().join(sep), files.join(sep));
1074+
1075+
RUN(Args() << "show" << "CLIPBOARD", "");
1076+
RUN(Args() << "unload" << tab1, tab1 + "\n");
1077+
1078+
QCOMPARE(dir1.files().join(sep), files.join(sep));
1079+
1080+
RUN("config" << "tab_encryption_enabled" << "true", "true\n");
1081+
1082+
RUN(args << inspect,
1083+
"0: itemsync-basename:test1 text/plain:TEXT1 ;; "
1084+
"1: itemsync-basename:test2 test-zzz:YYY text/plain:TEXT2");
1085+
QCOMPARE(dir1.files().join(sep), files.join(sep));
1086+
}

src/tests/itemsynctests.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ private slots:
5959

6060
void copyFiles();
6161

62+
void encryptionShouldNotAffectFiles();
63+
6264
private:
6365
TestInterfacePtr m_test;
6466
};

0 commit comments

Comments
 (0)