13#include "ui_importkeydialog.h"
15#include <QApplication>
21#include <QPlainTextEdit>
22#include <QRegularExpression>
32static const QRegularExpression
33 IMPORT_OK_RE(QStringLiteral(R
"(\[GNUPG:\] IMPORT_OK \d+ ([0-9A-Fa-f]+))"));
36static const QRegularExpression
37 IMPORTED_RE(QStringLiteral(R
"(\[GNUPG:\] IMPORTED ([0-9A-Fa-f]{16}))"));
40static const QRegularExpression KEY_IMPORTED_FALLBACK(
41 QStringLiteral(R
"(gpg: key ([0-9A-Fa-f]{40}|[0-9A-Fa-f]{16}):.*imported)"));
47 ui->importButton->setEnabled(
false);
57 const QString fileName = QFileDialog::getOpenFileName(
58 this, tr(
"Import GPG key"), QString(),
59 tr(
"ASCII-armored GPG key") +
" (*.asc);;" + tr(
"All files") +
" (*)");
61 if (fileName.isEmpty()) {
67 constexpr qint64 kMaxKeyFileBytes = 16LL * 1024 * 1024;
73 const QString target = QFileInfo(fileName).canonicalFilePath();
76 tr(
"Could not open file: %1").arg(fileName));
82 const bool tooLarge = file.size() > kMaxKeyFileBytes;
83 const QByteArray bytes =
84 tooLarge ? QByteArray() : file.read(kMaxKeyFileBytes + 1);
86 if (tooLarge || bytes.size() > kMaxKeyFileBytes) {
88 this, tr(
"Import key"),
89 tr(
"%1 is larger than a key file can be.").arg(fileName));
96 if (!bytes.trimmed().startsWith(
"-----BEGIN PGP")) {
100 this, tr(
"Import key"),
101 tr(
"%1 does not look like an ASCII-armored GPG key. Convert it with "
102 "<code>gpg --armor --export</code> first, or paste the armored "
103 "block via <b>From clipboard</b>.")
104 .arg(fileName.toHtmlEscaped()));
108 ui->inputTextEdit->setPlainText(QString::fromUtf8(bytes));
112 const QClipboard *clipboard = QApplication::clipboard();
113 const QString text = clipboard->text();
114 if (text.isEmpty()) {
119 ui->inputTextEdit->setPlainText(text);
123 const QString input =
ui->inputTextEdit->toPlainText().trimmed();
124 if (input.isEmpty()) {
134 const bool hasInput = !
ui->inputTextEdit->toPlainText().trimmed().isEmpty();
135 ui->importButton->setEnabled(hasInput);
140 QStringList args = {
"--status-fd",
"1",
"--import",
"--batch",
"--yes"};
148 showError(tr(
"GPG import failed:\n%1").arg(stdErr));
153 if (keyId.isEmpty()) {
157 if (keyId.isEmpty()) {
158 showError(tr(
"Could not parse imported key id from GPG output."));
168 const QStringList lines = output.split(
'\n', Qt::SkipEmptyParts);
176 for (
const QString &line : lines) {
177 const QRegularExpressionMatch match = IMPORT_OK_RE.match(line);
178 if (match.hasMatch()) {
179 return match.captured(1);
182 for (
const QString &line : lines) {
183 const QRegularExpressionMatch match = IMPORTED_RE.match(line);
184 if (match.hasMatch()) {
185 return match.captured(1);
188 for (
const QString &line : lines) {
189 const QRegularExpressionMatch match = KEY_IMPORTED_FALLBACK.match(line);
190 if (match.hasMatch()) {
191 return match.captured(1);
204 QMessageBox::information(
this, tr(
"Import key"),
205 tr(
"Public key %1 imported. Check its fingerprint "
206 "with its owner before encrypting to it: "
207 "importing a key says nothing about who holds "
static auto executeBlocking(const QString &app, const QStringList &args, const QString &input=QString(), QString *process_out=nullptr, QString *process_err=nullptr) -> int
Run a command synchronously and return its exit code.
~ImportKeyDialog() override
Destroys the dialog and its generated UI.
static auto parseGpgImportOutput(const QString &output) -> QString
Parse GPG import output to extract a key id or fingerprint.
void showError(const QString &message)
Shows message in a plain-text warning box (gpg's stderr quotes user IDs, which must not be read as ma...
auto importedKeyId() const -> QString
Get the imported key id on success.
QString m_importedKeyId
Key id or fingerprint of the last successful import; empty until then. Read back through importedKeyI...
void on_fileButton_clicked()
"From file" button: picks a file, reads it as a regular file of at most 16 MiB, requires the -----BEG...
ImportKeyDialog(const QString &gpgExe, QWidget *parent=nullptr)
Construct an ImportKeyDialog.
QString m_gpgExe
Path of the gpg binary to run; empty means plain gpg on PATH.
void on_pasteButton_clicked()
"From clipboard" button: replaces the input field with the clipboard text, unless the clipboard is em...
void showSuccess(const QString &keyId)
Shows the "imported, now verify the fingerprint" information box.
QScopedPointer< Ui::ImportKeyDialog > ui
Generated form (importkeydialog.ui); owned.
auto importFromString(const QString &input) -> bool
Feeds input to gpg --status-fd 1 --import --batch --yes via Executor::executeBlocking() and records t...
void on_importButton_clicked()
"Import" button: runs importFromString() on the trimmed input and closes the dialog with accept() on ...
void on_inputTextEdit_textChanged()
Enables the Import button only while the input field holds non-blank text.
static auto openRegularFile(const QString &path, QFile &file) -> bool
Open path for reading as the regular file it is, not as what a link under that name points to: the ob...
Executor, the FIFO queue every gpg, git and pass child process of QtPass goes through,...
Dialog that imports an ASCII-armored GPG public key from a file or the clipboard by running gpg --imp...
void warning(QWidget *parent, const QString &title, const QString &text)
Show a warning in plain text.
QMessageBox helpers that force Qt::PlainText so store-controlled names can never be read as rich-text...
Util: static helpers for the process environment, binary discovery, store paths, URL safety and the l...