diff options
Diffstat (limited to 'src/core/function/gpg/GpgKeyManager.cpp')
-rw-r--r-- | src/core/function/gpg/GpgKeyManager.cpp | 237 |
1 files changed, 182 insertions, 55 deletions
diff --git a/src/core/function/gpg/GpgKeyManager.cpp b/src/core/function/gpg/GpgKeyManager.cpp index bd52c341..753b668f 100644 --- a/src/core/function/gpg/GpgKeyManager.cpp +++ b/src/core/function/gpg/GpgKeyManager.cpp @@ -36,6 +36,64 @@ GpgFrontend::GpgKeyManager::GpgKeyManager(int channel) : SingletonFunctionObject<GpgKeyManager>(channel) {} +auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle, + const char* status, + const char* args, + int fd) -> gpgme_error_t { + auto* handle_struct = static_cast<AutomatonHandelStruct*>(handle); + QString status_s = status; + QString args_s = args; + + if (status_s == "KEY_CONSIDERED") { + auto tokens = QString(args).split(' '); + + if (tokens.empty() || tokens[0] != handle_struct->KeyFpr()) { + LOG_W() << "handle struct key fpr " << handle_struct->KeyFpr() + << "mismatch token: " << tokens[0] << ", exit..."; + + return -1; + } + + return 0; + } + + if (status_s == "GOT_IT" || status_s.isEmpty()) { + FLOG_D("gpg reply is GOT_IT, continue..."); + return 0; + } + + LOG_D() << "current state" << handle_struct->CurrentStatus() + << "gpg status: " << status_s << ", args: " << args_s; + + AutomatonState next_state = handle_struct->NextState(status_s, args_s); + if (next_state == AS_ERROR) { + FLOG_D("handle struct next state caught error, abort..."); + return -1; + } + LOG_D() << "next state" << next_state; + + if (next_state == AS_SAVE) { + handle_struct->SetSuccess(true); + } + + // set state and preform action + handle_struct->SetStatus(next_state); + Command cmd = handle_struct->Action(); + + LOG_D() << "next action, cmd:" << cmd; + + if (!cmd.isEmpty()) { + auto btye_array = cmd.toUtf8(); + gpgme_io_write(fd, btye_array, btye_array.size()); + gpgme_io_write(fd, "\n", 1); + } else if (status_s == "GET_LINE") { + // avoid trapping in this state + return GPG_ERR_FALSE; + } + + return 0; +} + auto GpgFrontend::GpgKeyManager::SignKey( const GpgFrontend::GpgKey& target, GpgFrontend::KeyArgsList& keys, const QString& uid, const std::unique_ptr<QDateTime>& expires) -> bool { @@ -179,69 +237,110 @@ auto GpgFrontend::GpgKeyManager::SetOwnerTrustLevel(const GpgKey& key, return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success(); } -auto GpgFrontend::GpgKeyManager::interactor_cb_fnc(void* handle, - const char* status, - const char* args, - int fd) -> gpgme_error_t { - auto* handle_struct = static_cast<AutomatonHandelStruct*>(handle); - QString status_s = status; - QString args_s = args; - - if (status_s == "KEY_CONSIDERED") { - auto tokens = QString(args).split(' '); - - if (tokens.empty() || tokens[0] != handle_struct->KeyFpr()) { - LOG_W() << "handle struct key fpr " << handle_struct->KeyFpr() - << "mismatch token: " << tokens[0] << ", exit..."; - - return -1; - } - - return 0; - } - - if (status_s == "GOT_IT" || status_s.isEmpty()) { - FLOG_D("status GOT_IT, continue..."); - return 0; +auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key, + int subkey_index) -> bool { + if (subkey_index < 0 || subkey_index >= key.GetSubKeys()->size()) { + LOG_W() << "illegal subkey index: " << subkey_index; } - LOG_D() << "current state" << handle_struct->CurrentStatus() - << "gpg status: " << status_s << ", args: " << args_s; - - AutomatonState next_state = handle_struct->NextState(status_s, args_s); - if (next_state == AS_ERROR) { - FLOG_D("handle struct next state caught error, abort..."); - return -1; - } - LOG_D() << "next state" << next_state; + AutomatonNextStateHandler next_state_handler = + [](AutomatonState state, QString status, QString args) { + auto tokens = args.split(' '); - if (next_state == AS_SAVE) { - handle_struct->SetSuccess(true); - } + switch (state) { + case AS_START: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return AS_SELECT; + } + return AS_ERROR; + case AS_SELECT: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return AS_COMMAND; + } + return AS_ERROR; + case AS_COMMAND: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return AS_QUIT; + } else if (status == "GET_BOOL" && + args == "keyedit.remove.subkey.okay") { + return AS_REALLY_ULTIMATE; + } + return AS_ERROR; + case AS_REALLY_ULTIMATE: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return AS_QUIT; + } + return AS_ERROR; + case AS_QUIT: + if (status == "GET_BOOL" && args == "keyedit.save.okay") { + return AS_SAVE; + } + return AS_ERROR; + case AS_ERROR: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return AS_QUIT; + } + return AS_ERROR; + default: + return AS_ERROR; + }; + }; - // set state and preform action - handle_struct->SetStatus(next_state); - Command cmd = handle_struct->Action(); + AutomatonActionHandler action_handler = + [subkey_index](AutomatonHandelStruct& handler, AutomatonState state) { + switch (state) { + case AS_SELECT: + return QString("key %1").arg(subkey_index); + case AS_COMMAND: + return QString("delkey"); + case AS_REALLY_ULTIMATE: + handler.SetSuccess(true); + return QString("Y"); + case AS_QUIT: + return QString("quit"); + case AS_SAVE: + handler.SetSuccess(true); + return QString("Y"); + case AS_START: + case AS_ERROR: + return QString(""); + default: + return QString(""); + } + return QString(""); + }; - LOG_D() << "next action, cmd:" << cmd; + auto key_fpr = key.GetFingerprint(); + AutomatonHandelStruct handel_struct(key_fpr); + handel_struct.SetHandler(next_state_handler, action_handler); - if (!cmd.isEmpty()) { - auto btye_array = cmd.toUtf8(); - gpgme_io_write(fd, btye_array, btye_array.size()); - gpgme_io_write(fd, "\n", 1); - } else if (status_s == "GET_LINE") { - // avoid trapping in this state - return GPG_ERR_FALSE; - } + GpgData data_out; - return 0; + auto err = + gpgme_op_interact(ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), 0, + GpgKeyManager::interactor_cb_fnc, + static_cast<void*>(&handel_struct), data_out); + return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success(); } -auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key, - int subkey_index) -> bool { + +auto GpgFrontend::GpgKeyManager::RevokeSubkey( + const GpgKey& key, int subkey_index, int reason_code, + const QString& reason_text) -> bool { if (subkey_index < 0 || subkey_index >= key.GetSubKeys()->size()) { LOG_W() << "illegal subkey index: " << subkey_index; } + // dealing with reason text +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 4) + auto reason_text_lines = + GpgFrontend::SecureCreateSharedObject<QList<QString>>( + reason_text.split('\n', Qt::SkipEmptyParts).toVector()); +#else + auto reason_text_lines = + GpgFrontend::SecureCreateSharedObject<QVector<QString>>( + reason_text.split('\n', Qt::SkipEmptyParts).toVector()); +#endif + AutomatonNextStateHandler next_state_handler = [](AutomatonState state, QString status, QString args) { auto tokens = args.split(' '); @@ -261,13 +360,35 @@ auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key, if (status == "GET_LINE" && args == "keyedit.prompt") { return AS_QUIT; } else if (status == "GET_BOOL" && - args == "keyedit.remove.subkey.okay") { + args == "keyedit.revoke.subkey.okay") { + return AS_REALLY_ULTIMATE; + } + return AS_ERROR; + case AS_REASON_CODE: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return AS_QUIT; + } else if (status == "GET_LINE" && + args == "ask_revocation_reason.text") { + return AS_REASON_TEXT; + } + return AS_ERROR; + case AS_REASON_TEXT: + if (status == "GET_LINE" && args == "keyedit.prompt") { + return AS_QUIT; + } else if (status == "GET_LINE" && + args == "ask_revocation_reason.text") { + return AS_REASON_TEXT; + } else if (status == "GET_BOOL" && + args == "ask_revocation_reason.okay") { return AS_REALLY_ULTIMATE; } return AS_ERROR; case AS_REALLY_ULTIMATE: if (status == "GET_LINE" && args == "keyedit.prompt") { return AS_QUIT; + } else if (status == "GET_LINE" && + args == "ask_revocation_reason.code") { + return AS_REASON_CODE; } return AS_ERROR; case AS_QUIT: @@ -286,14 +407,20 @@ auto GpgFrontend::GpgKeyManager::DeleteSubkey(const GpgKey& key, }; AutomatonActionHandler action_handler = - [subkey_index](AutomatonHandelStruct& handler, AutomatonState state) { + [subkey_index, reason_code, reason_text_lines]( + AutomatonHandelStruct& handler, AutomatonState state) { switch (state) { case AS_SELECT: return QString("key %1").arg(subkey_index); case AS_COMMAND: - return QString("delkey"); + return QString("revkey"); + case AS_REASON_CODE: + return QString::number(reason_code); + case AS_REASON_TEXT: + return reason_text_lines->isEmpty() + ? QString("") + : QString(reason_text_lines->takeFirst().toUtf8()); case AS_REALLY_ULTIMATE: - handler.SetSuccess(true); return QString("Y"); case AS_QUIT: return QString("quit"); |