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

IJHack / QtPass / 24938165024

25 Apr 2026 06:54PM UTC coverage: 27.433% (+0.6%) from 26.828%
24938165024

push

github

web-flow
feat: wire Share submenu actions to real operations (#422) (#1162)

* feat: wire Share submenu actions to real operations (#422)

Replace the help-only stubs added in #1144 with real behavior:

- Export my public key now runs `gpg --armor --export <signing-key>`
  via Executor and shows the armored output in a new dedicated
  ExportPublicKeyDialog, with Copy-to-Clipboard and Save-to-File
  actions. Falls back to the original guidance dialog when no signing
  key is configured.
- Add recipient now opens the existing UsersDialog for the folder so
  recipients can be ticked/unticked through the standard UI.

Part of issue #422.

* fix: address review on share submenu actions

- Use real years (2026) in SPDX headers of new files; matches the
  rest of the repo and replaces the YYYY placeholder.
- Split getPassSigningKey() on whitespace before passing key IDs to
  gpg --export so multi-key signing configurations work, mirroring the
  pattern used in imitatepass.cpp / pass.cpp.
- Switch the save path in ExportPublicKeyDialog to QSaveFile and check
  QTextStream::status() and commit() so a partial/failed write surfaces
  to the user instead of silently truncating.
- Give the Copy button transient feedback by relabelling it to
  "Copied!" for 1.5s via QTimer::singleShot.

* fix: harden ExportPublicKeyDialog against unsafe key IDs

- Sanitize the key ID before using it as the default save name: take
  only the first whitespace-separated token, then strip everything
  outside [A-Za-z0-9_-]. With the multi-key fix in fa77fa9 the stored
  identity can hold several space-separated IDs, so the raw value is
  no longer a clean filename.
- Force keyIdLabel to Qt::PlainText so a settings-controlled key ID
  can never be rendered as rich text. Matches the textFormat property
  used on UsersDialog's label.

* test: cover ExportPublicKeyDialog and harden against unsafe key IDs

Restores patch coverage for #1162 (codecov was 0% for the new files)
and addresses review feed... (continued)

25 of 66 new or added lines in 3 files covered. (37.88%)

11 existing lines in 2 files now uncovered.

1711 of 6237 relevant lines covered (27.43%)

28.41 hits per line

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

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

6
#include <QApplication>
7
#include <QClipboard>
8
#include <QFileDialog>
9
#include <QFontDatabase>
10
#include <QMessageBox>
11
#include <QRegularExpression>
12
#include <QSaveFile>
13
#include <QTextStream>
14
#include <QTimer>
15

16
/**
17
 * @brief ExportPublicKeyDialog::ExportPublicKeyDialog populate the dialog
18
 *        with the supplied armored key text.
19
 * @param keyId GPG key identifier shown in the header.
20
 * @param armoredKey ASCII-armored public key text.
21
 * @param parent Optional parent widget.
22
 */
23
ExportPublicKeyDialog::ExportPublicKeyDialog(const QString &keyId,
3✔
24
                                             const QString &armoredKey,
25
                                             QWidget *parent)
3✔
26
    : QDialog(parent), ui(new Ui::ExportPublicKeyDialog), m_keyId(keyId) {
3✔
27
  ui->setupUi(this);
3✔
28
  ui->keyIdLabel->setText(tr("Public key for %1").arg(keyId));
6✔
29
  ui->plainTextEdit->setFont(
3✔
30
      QFontDatabase::systemFont(QFontDatabase::FixedFont));
6✔
31
  ui->plainTextEdit->setPlainText(armoredKey);
3✔
32
  m_copyButtonOriginalText = ui->copyButton->text();
3✔
33
}
3✔
34

35
/**
36
 * @brief ExportPublicKeyDialog::~ExportPublicKeyDialog basic destructor.
37
 */
38
ExportPublicKeyDialog::~ExportPublicKeyDialog() { delete ui; }
6✔
39

40
/**
41
 * @brief ExportPublicKeyDialog::sanitizeKeyIdForFilename keep only the
42
 *        first whitespace-separated token of keyId and strip any character
43
 *        that is not in [A-Za-z0-9_-].
44
 * @param keyId Raw identifier (possibly multi-key, possibly user-supplied).
45
 * @return Filename-safe token, empty if no characters survive.
46
 */
47
auto ExportPublicKeyDialog::sanitizeKeyIdForFilename(const QString &keyId)
10✔
48
    -> QString {
49
  static const QRegularExpression whitespace(QStringLiteral("\\s+"));
11✔
50
  static const QRegularExpression unsafeChars(QStringLiteral("[^A-Za-z0-9_-]"));
11✔
51
  const QStringList tokens = keyId.split(whitespace, Qt::SkipEmptyParts);
10✔
52
  if (tokens.isEmpty()) {
10✔
53
    return QString();
54
  }
55
  QString token = tokens.first();
56
  token.remove(unsafeChars);
8✔
57
  return token;
58
}
59

60
/**
61
 * @brief ExportPublicKeyDialog::on_copyButton_clicked copy the armored key
62
 *        text to the system clipboard and briefly relabel the button so the
63
 *        user gets visible feedback.
64
 */
65
void ExportPublicKeyDialog::on_copyButton_clicked() {
1✔
66
  QApplication::clipboard()->setText(ui->plainTextEdit->toPlainText());
1✔
67
  ui->copyButton->setText(tr("Copied!"));
1✔
68
  QTimer::singleShot(1500, this, [this]() {
1✔
69
    ui->copyButton->setText(m_copyButtonOriginalText);
1✔
70
  });
1✔
71
}
1✔
72

73
/**
74
 * @brief ExportPublicKeyDialog::on_saveButton_clicked prompt for a
75
 *        destination and write the armored key to that file.
76
 */
NEW
77
void ExportPublicKeyDialog::on_saveButton_clicked() {
×
NEW
78
  QString safeKeyId = sanitizeKeyIdForFilename(m_keyId);
×
79
  QString defaultName = safeKeyId.isEmpty()
NEW
80
                            ? QStringLiteral("public_key.asc")
×
NEW
81
                            : QStringLiteral("%1.asc").arg(safeKeyId);
×
82
  QString fileName = QFileDialog::getSaveFileName(
NEW
83
      this, tr("Save Public Key"), defaultName,
×
NEW
84
      tr("ASCII-armored key (*.asc);;All files (*)"));
×
NEW
85
  if (fileName.isEmpty()) {
×
86
    return;
87
  }
NEW
88
  QSaveFile file(fileName);
×
NEW
89
  if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
×
NEW
90
    QMessageBox::warning(this, tr("Save Public Key"),
×
NEW
91
                         tr("Could not open %1 for writing: %2")
×
NEW
92
                             .arg(fileName, file.errorString()));
×
93
    return;
94
  }
NEW
95
  QTextStream out(&file);
×
NEW
96
  out << ui->plainTextEdit->toPlainText();
×
NEW
97
  out.flush();
×
NEW
98
  if (out.status() != QTextStream::Ok || !file.commit()) {
×
NEW
99
    QMessageBox::warning(
×
NEW
100
        this, tr("Save Public Key"),
×
NEW
101
        tr("Could not write to %1: %2").arg(fileName, file.errorString()));
×
102
  }
NEW
103
}
×
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