aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/m_key_server_sync/KeyServerBatchLogic.cpp8
-rw-r--r--src/m_key_server_sync/KeyServerBatchLogic.h5
-rw-r--r--src/m_key_server_sync/KeyServerSyncModule.cpp57
-rw-r--r--src/m_key_server_sync/module.json1
-rw-r--r--src/m_key_server_sync/test/KeyServerBatchLogicTest.cpp6
5 files changed, 68 insertions, 9 deletions
diff --git a/src/m_key_server_sync/KeyServerBatchLogic.cpp b/src/m_key_server_sync/KeyServerBatchLogic.cpp
index 77411bc..18588eb 100644
--- a/src/m_key_server_sync/KeyServerBatchLogic.cpp
+++ b/src/m_key_server_sync/KeyServerBatchLogic.cpp
@@ -102,4 +102,12 @@ auto PublishSummary(int published, int total, const QString& host,
failures);
}
+auto InterruptedSummary(int handled, int total) -> QString {
+ return GTrC::tr("The key databases were changed while this was running, so "
+ "it was stopped after %1 of %2 keys. Nothing fetched was "
+ "imported. Please start it again.")
+ .arg(handled)
+ .arg(total);
+}
+
} // namespace KeyServerBatchLogic
diff --git a/src/m_key_server_sync/KeyServerBatchLogic.h b/src/m_key_server_sync/KeyServerBatchLogic.h
index 60ea682..9fe8612 100644
--- a/src/m_key_server_sync/KeyServerBatchLogic.h
+++ b/src/m_key_server_sync/KeyServerBatchLogic.h
@@ -83,4 +83,9 @@ auto RefreshSummary(int fetched, int total, const QStringList& failures)
auto PublishSummary(int published, int total, const QString& host,
const QStringList& failures) -> QString;
+/// What a batch says when the key databases were reloaded under it: it was
+/// stopped after @p handled of @p total keys, and nothing it fetched was
+/// imported, since the database it was started on is no longer the one in use.
+auto InterruptedSummary(int handled, int total) -> QString;
+
} // namespace KeyServerBatchLogic
diff --git a/src/m_key_server_sync/KeyServerSyncModule.cpp b/src/m_key_server_sync/KeyServerSyncModule.cpp
index db96622..f7ba7f4 100644
--- a/src/m_key_server_sync/KeyServerSyncModule.cpp
+++ b/src/m_key_server_sync/KeyServerSyncModule.cpp
@@ -174,6 +174,11 @@ using KeyServerBatchLogic::BatchKey;
/// Set when the module deactivates; every batch step checks it first.
std::atomic<bool> g_stopped{false};
+/// How many times the key databases were reloaded. A batch remembers the count
+/// it started at: its keys carry the channels of the databases in use then,
+/// which a reload retires, so it stops rather than act on whatever they name.
+std::atomic<int> g_key_database_reloads{0};
+
/**
* @brief Refreshes or publishes several keys, one request at a time.
*
@@ -191,7 +196,8 @@ class KeyServerBatch : public QObject {
KeyServerBatch(Kind kind, QList<BatchKey> keys)
: kind_(kind),
keys_(std::move(keys)),
- route_(KeyServerList::SyncRoute()) {}
+ route_(KeyServerList::SyncRoute()),
+ reloads_at_start_(g_key_database_reloads.load()) {}
/// @return false when the user declined, and the batch is already gone
auto Start() -> bool {
@@ -207,6 +213,7 @@ class KeyServerBatch : public QObject {
private:
void Next() {
if (g_stopped.load()) return;
+ if (g_key_database_reloads.load() != reloads_at_start_) return Interrupt();
if (index_ >= keys_.size()) return Finish();
const auto key = keys_[index_++];
if (kind_ == Kind::kRefresh) {
@@ -269,16 +276,31 @@ class KeyServerBatch : public QObject {
QTimer::singleShot(kSpacingMs, this, [this]() { Next(); });
}
+ void Interrupt() {
+ const auto handled = static_cast<int>(index_);
+ Tell(Severity::kWarning,
+ kind_ == Kind::kRefresh ? GTrC::tr("Key Refresh Stopped")
+ : GTrC::tr("Key Publishing Stopped"),
+ KeyServerBatchLogic::InterruptedSummary(
+ handled, static_cast<int>(keys_.size())));
+ deleteLater();
+ }
+
void Finish() {
const auto total = static_cast<int>(keys_.size());
+ if (kind_ == Kind::kRefresh && !blocks_.isEmpty() &&
+ gf::sdk::ImportKeys(GFModuleSdkContext(),
+ static_cast<int>(keys_.front().channel),
+ KeyServerBatchLogic::JoinForImport(blocks_)) < 0) {
+ // Refused: its database is gone. What was fetched is not in any
+ // keyring, so it is not reported as refreshed.
+ failures_.append(GTrC::tr("the fetched keys could not be imported: the "
+ "key database is no longer available"));
+ blocks_.clear();
+ }
const auto severity =
failures_.isEmpty() ? Severity::kInfo : Severity::kWarning;
if (kind_ == Kind::kRefresh) {
- if (!blocks_.isEmpty()) {
- gf::sdk::ImportKeys(GFModuleSdkContext(),
- static_cast<int>(keys_.front().channel),
- KeyServerBatchLogic::JoinForImport(blocks_));
- }
Tell(severity, GTrC::tr("Key Refresh Finished"),
KeyServerBatchLogic::RefreshSummary(static_cast<int>(blocks_.size()),
total, failures_));
@@ -297,6 +319,7 @@ class KeyServerBatch : public QObject {
int done_ = 0;
QList<QByteArray> blocks_;
QStringList failures_;
+ int reloads_at_start_;
};
/// The one batch that may run; a second request waits for it to finish.
@@ -470,8 +493,17 @@ auto UpdateKeyFromKeyServer(int channel, const QString& fpr) -> int {
FetchKey(
route, fpr, true,
- [channel](const QString& key_data) {
- gf::sdk::ImportKeys(GFModuleSdkContext(), channel, key_data.toUtf8());
+ [channel, fpr](const QString& key_data) {
+ if (gf::sdk::ImportKeys(GFModuleSdkContext(), channel,
+ key_data.toUtf8()) < 0) {
+ // Refused: the key database was reloaded while the key was being
+ // fetched, and the one it was asked for is gone.
+ Tell(Severity::kError, GTrC::tr("Key Update Failed"),
+ GTrC::tr("The public key was fetched but could not be imported, "
+ "because its key database is no longer available.\n"
+ "Key ID: %1")
+ .arg(fpr));
+ }
},
[fpr, host](const QString& error, const QString& data) {
Q_UNUSED(data);
@@ -762,6 +794,11 @@ auto OnRequestSearchPublicKeyByFingerprint(const GFEvent& event)
return GFEventResult::Ok();
}
+auto OnKeyDatabasesReloaded(const GFEvent&) -> GFEventResult {
+ ++g_key_database_reloads;
+ return GFEventResult::Ok();
+}
+
auto OnUnload() -> void {
// Said "paper key module" until now, copied from a module that no longer
// exists -- so the one line naming who was shutting down named the wrong one.
@@ -769,7 +806,9 @@ auto OnUnload() -> void {
}
// The module's whole framework surface.
-constexpr std::array<GFEventBinding, 4> kEvents = {{
+constexpr std::array<GFEventBinding, 5> kEvents = {{
+ // Batches already running stop, rather than act on retired channels.
+ {"KEY_DATABASES_RELOADED", &OnKeyDatabasesReloaded},
{"REQUEST_GET_PUBLIC_KEY_BY_FINGERPRINT",
&OnRequestGetPublicKeyByFingerprint},
{"REQUEST_GET_PUBLIC_KEY_BY_KEY_ID", &OnRequestGetPublicKeyByKeyId},
diff --git a/src/m_key_server_sync/module.json b/src/m_key_server_sync/module.json
index 0244b29..a497f01 100644
--- a/src/m_key_server_sync/module.json
+++ b/src/m_key_server_sync/module.json
@@ -13,6 +13,7 @@
"ui.custom"
],
"events": [
+ "KEY_DATABASES_RELOADED",
"REQUEST_GET_PUBLIC_KEY_BY_FINGERPRINT",
"REQUEST_GET_PUBLIC_KEY_BY_KEY_ID",
"REQUEST_SEARCH_PUBLIC_KEY_BY_FINGERPRINT",
diff --git a/src/m_key_server_sync/test/KeyServerBatchLogicTest.cpp b/src/m_key_server_sync/test/KeyServerBatchLogicTest.cpp
index 9359410..bdb461a 100644
--- a/src/m_key_server_sync/test/KeyServerBatchLogicTest.cpp
+++ b/src/m_key_server_sync/test/KeyServerBatchLogicTest.cpp
@@ -98,6 +98,12 @@ TEST(KeyServerBatchLogicTest, SummariesCountAndListFailures) {
EXPECT_TRUE(published.contains("keys.openpgp.org"));
}
+TEST(KeyServerBatchLogicTest, AnInterruptedBatchSaysHowFarItGot) {
+ const auto text = KeyServerBatchLogic::InterruptedSummary(2, 5);
+ EXPECT_TRUE(text.contains("2 of 5"));
+ EXPECT_TRUE(text.contains("Nothing fetched was imported"));
+}
+
// "The server does not have this key" is an answer, told apart from a real
// failure by the caller, and never shown in the transport's own wording.
TEST(KeyServerBatchLogicTest, NotFoundIsItsOwnPlainAnswer) {