• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IJHack / QtPass / 27692393037

17 Jun 2026 01:26PM UTC coverage: 57.221% (-0.2%) from 57.47%
27692393037

Pull #1553

github

web-flow
Merge 9051e6041 into 311601814
Pull Request #1553: refactor(#1511): inject Pass*/AppSettings into dialog layer

11 of 31 new or added lines in 5 files covered. (35.48%)

19 existing lines in 4 files now uncovered.

3966 of 6931 relevant lines covered (57.22%)

23.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

41.46
/src/importkeydialog.cpp
1
// SPDX-FileCopyrightText: 2026 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "importkeydialog.h"
4

5
#include "executor.h"
6
#include "ui_importkeydialog.h"
7

8
#include <QApplication>
9
#include <QClipboard>
10
#include <QFile>
11
#include <QFileDialog>
12
#include <QMessageBox>
13
#include <QPlainTextEdit>
14
#include <QRegularExpression>
15
#include <QString>
16

17
// Locale-independent: gpg's machine-readable status output via --status-fd 1.
18
// See doc/DETAILS in the GnuPG source for IMPORT_OK / IMPORTED grammar.
19
static const QRegularExpression
20
    IMPORT_OK_RE(QStringLiteral(R"(\[GNUPG:\] IMPORT_OK \d+ ([0-9A-Fa-f]+))"));
21
static const QRegularExpression
22
    IMPORTED_RE(QStringLiteral(R"(\[GNUPG:\] IMPORTED ([0-9A-Fa-f]{16}))"));
23
// Fallback for the human-readable (English-locale) line.
24
static const QRegularExpression KEY_IMPORTED_FALLBACK(
25
    QStringLiteral(R"(gpg: key ([0-9A-Fa-f]{40}|[0-9A-Fa-f]{16}):.*imported)"));
26

27
ImportKeyDialog::ImportKeyDialog(const QString &gpgExe, QWidget *parent)
5✔
28
    : QDialog(parent), ui(new Ui::ImportKeyDialog), m_gpgExe(gpgExe) {
5✔
29
  ui->setupUi(this);
5✔
30
  ui->importButton->setEnabled(false);
5✔
31
}
5✔
32

33
ImportKeyDialog::~ImportKeyDialog() = default;
10✔
34

35
auto ImportKeyDialog::importedKeyId() const -> QString {
1✔
36
  return m_importedKeyId;
1✔
37
}
38

39
void ImportKeyDialog::on_fileButton_clicked() {
×
40
  const QString fileName = QFileDialog::getOpenFileName(
41
      this, tr("Import GPG Key"), QString(),
×
42
      tr("ASCII-armored GPG key") + " (*.asc);;" + tr("All Files") + " (*)");
×
43

44
  if (fileName.isEmpty()) {
×
45
    return;
46
  }
47

48
  QFile file(fileName);
×
49
  if (!file.open(QIODevice::ReadOnly)) {
×
50
    QMessageBox::warning(this, tr("Import Key"),
×
51
                         tr("Could not open file: %1").arg(fileName));
×
52
    return;
×
53
  }
54

55
  const QByteArray bytes = file.readAll();
×
56
  file.close();
×
57

58
  // Only ASCII-armored content is shown in the text edit; binary keyrings
59
  // would lose bytes through UTF-8 conversion. Reject anything that doesn't
60
  // start with the PGP armor header.
61
  if (!bytes.trimmed().startsWith("-----BEGIN PGP")) {
×
62
    // Message body is rich text (uses <code>/<b>); escape the path so any
63
    // characters in it cannot reach the HTML subset Qt renders.
64
    QMessageBox::warning(
×
65
        this, tr("Import Key"),
×
66
        tr("%1 does not look like an ASCII-armored GPG key. Convert it with "
×
67
           "<code>gpg --armor --export</code> first, or paste the armored "
68
           "block via <b>From Clipboard</b>.")
69
            .arg(fileName.toHtmlEscaped()));
×
70
    return;
71
  }
72

73
  ui->inputTextEdit->setPlainText(QString::fromUtf8(bytes));
×
74
}
×
75

76
void ImportKeyDialog::on_pasteButton_clicked() {
1✔
77
  const QClipboard *clipboard = QApplication::clipboard();
1✔
78
  const QString text = clipboard->text();
1✔
79
  if (text.isEmpty()) {
1✔
80
    // Don't wipe whatever the user already typed/loaded for an empty
81
    // clipboard.
82
    return;
83
  }
84
  ui->inputTextEdit->setPlainText(text);
1✔
85
}
86

87
void ImportKeyDialog::on_importButton_clicked() {
×
88
  const QString input = ui->inputTextEdit->toPlainText().trimmed();
×
89
  if (input.isEmpty()) {
×
90
    return;
91
  }
92

93
  if (importFromString(input)) {
×
94
    accept();
×
95
  }
96
}
97

98
void ImportKeyDialog::on_inputTextEdit_textChanged() {
3✔
99
  const bool hasInput = !ui->inputTextEdit->toPlainText().trimmed().isEmpty();
6✔
100
  ui->importButton->setEnabled(hasInput);
3✔
101
}
3✔
102

103
bool ImportKeyDialog::importFromString(const QString &input) {
×
NEW
104
  QString gpgExe = m_gpgExe.isEmpty() ? QStringLiteral("gpg") : m_gpgExe;
×
105
  QStringList args = {"--status-fd", "1", "--import", "--batch", "--yes"};
×
106

107
  QString stdOut;
×
108
  QString stdErr;
×
109

110
  int result = Executor::executeBlocking(gpgExe, args, input, &stdOut, &stdErr);
×
111

112
  if (result != 0) {
×
113
    showError(tr("GPG import failed:\n%1").arg(stdErr.toHtmlEscaped()));
×
114
    return false;
×
115
  }
116

117
  QString keyId = parseGpgImportOutput(stdOut);
×
118
  if (keyId.isEmpty()) {
×
119
    keyId = parseGpgImportOutput(stdErr);
×
120
  }
121

122
  if (keyId.isEmpty()) {
×
123
    showError(tr("Could not parse imported key id from GPG output."));
×
124
    return false;
×
125
  }
126

127
  m_importedKeyId = keyId;
×
128
  showSuccess(keyId);
×
129
  return true;
130
}
×
131

132
auto ImportKeyDialog::parseGpgImportOutput(const QString &output) -> QString {
26✔
133
  const QStringList lines = output.split('\n', Qt::SkipEmptyParts);
26✔
134
  // The order here is the priority order: prefer the locale-independent
135
  // status lines over the English human-readable line, and within the
136
  // status lines prefer IMPORT_OK (full fingerprint) over IMPORTED
137
  // (long key id). Each regex is scanned across every line before we
138
  // fall through to the next regex; the per-line ordering of gpg's
139
  // output (which emits IMPORTED before IMPORT_OK) must not pick the
140
  // weaker identifier.
141
  for (const QString &line : lines) {
44✔
142
    const QRegularExpressionMatch match = IMPORT_OK_RE.match(line);
29✔
143
    if (match.hasMatch()) {
29✔
144
      return match.captured(1);
11✔
145
    }
146
  }
29✔
147
  for (const QString &line : lines) {
28✔
148
    const QRegularExpressionMatch match = IMPORTED_RE.match(line);
15✔
149
    if (match.hasMatch()) {
15✔
150
      return match.captured(1);
2✔
151
    }
152
  }
15✔
153
  for (const QString &line : lines) {
21✔
154
    const QRegularExpressionMatch match = KEY_IMPORTED_FALLBACK.match(line);
13✔
155
    if (match.hasMatch()) {
13✔
156
      return match.captured(1);
5✔
157
    }
158
  }
13✔
159
  return {};
160
}
161

162
void ImportKeyDialog::showError(const QString &message) {
×
163
  QMessageBox::warning(this, tr("Import Key"), message);
×
164
}
×
165

166
void ImportKeyDialog::showSuccess(const QString &keyId) {
×
167
  QMessageBox::information(this, tr("Import Key"),
×
168
                           tr("Successfully imported key: %1").arg(keyId));
×
169
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc