Line data Source code
1 : // SPDX-FileCopyrightText: 2015 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : /**
4 : * @file
5 : * @brief Dialog that collects name, e-mail and passphrase for a new GPG
6 : * keypair and drives `gpg --gen-key --batch` through the Pass backend.
7 : */
8 : #ifndef SRC_KEYGENDIALOG_H_
9 : #define SRC_KEYGENDIALOG_H_
10 :
11 : #include <QDialog>
12 : #include <QScopedPointer>
13 : #include <memory>
14 :
15 : #include "qprogressindicator.h"
16 :
17 : namespace Ui {
18 : class KeygenDialog;
19 : }
20 :
21 : class Pass;
22 :
23 : /**
24 : * @class KeygenDialog
25 : * @ingroup keys
26 : * @brief Handles GPG keypair generation.
27 : *
28 : * The form asks for a name, an e-mail address and a passphrase typed twice
29 : * (or an explicit "no passphrase"); an Expert checkbox reveals the editable
30 : * GPG batch template that the name and e-mail fields are mirrored into. The
31 : * passphrase is never written into that template: on OK, done() validates
32 : * the fields, splices the passphrase in with applyPassphrase() and hands the
33 : * batch to Pass::GenerateGPGKeys(), replacing the form with a progress
34 : * indicator. The dialog owns nothing of the backend; it connects to
35 : * Pass::finishedGenerateGPGKeys (accept) and Pass::generateGPGKeysFailed
36 : * (show the error and give the form back) for the duration of one attempt
37 : * only, so an `exec()` caller such as the first-run wizard sees `Accepted`
38 : * exactly when a key exists.
39 : */
40 : class KeygenDialog : public QDialog {
41 10 : Q_OBJECT
42 :
43 : public:
44 : /**
45 : * @brief Construct a KeygenDialog.
46 : * @param gpgExe Path to the gpg executable (for the default template).
47 : * @param pass Backend that runs `gpg --gen-key`; the dialog accepts on its
48 : * finishedGenerateGPGKeys() and shows generateGPGKeysFailed().
49 : * @param parent Parent widget, or nullptr.
50 : */
51 : explicit KeygenDialog(const QString &gpgExe, Pass *pass,
52 : QWidget *parent = nullptr);
53 : /** @brief Destroys the dialog and its generated UI. */
54 : ~KeygenDialog() override;
55 :
56 : /**
57 : * @brief Splice a passphrase into a GPG batch template.
58 : *
59 : * Replaces %no-protection (or an existing Passphrase: line) with
60 : * "Passphrase: <passphrase>"; an empty passphrase yields %no-protection.
61 : * @param batch GPG batch template text.
62 : * @param passphrase Key passphrase, empty for an unprotected key.
63 : * @return Batch text ready for gpg --gen-key --batch.
64 : */
65 : static QString applyPassphrase(const QString &batch,
66 : const QString &passphrase);
67 :
68 : /**
69 : * @brief Whether @p value can go into a GPG batch line as one value.
70 : *
71 : * The batch is line-oriented, and gpg reads a line as a C string: a line
72 : * break or NUL in a name or passphrase would start a directive of its own
73 : * (%no-protection, a Passphrase: of the paster's choosing).
74 : * @param value A field's text.
75 : * @return False when it holds a CR, LF or NUL.
76 : */
77 : [[nodiscard]] static auto isBatchValue(const QString &value) -> bool;
78 :
79 : protected:
80 : private slots:
81 : /**
82 : * @brief First passphrase field edited: re-evaluates whether OK may be
83 : * enabled. The text itself stays out of the template box.
84 : * @param arg1 The new field text (unused).
85 : */
86 : void on_passphrase1_textChanged(const QString &arg1);
87 : /**
88 : * @brief Confirmation field edited: same as on_passphrase1_textChanged().
89 : * @param arg1 The new field text (unused).
90 : */
91 : void on_passphrase2_textChanged(const QString &arg1);
92 : /**
93 : * @brief Ticking "no passphrase" clears and disables the passphrase
94 : * fields and makes OK available; unticking it asks for one again.
95 : * @param checked The checkbox state.
96 : */
97 : void setNoPassphrase(bool checked);
98 : /**
99 : * @brief Enable or disable expert mode (the editable batch template).
100 : *
101 : * Connected explicitly to QCheckBox::toggled in the constructor; the box
102 : * is two-state, so the deprecated stateChanged(int) is not needed.
103 : * @param checked true when the Expert box is checked.
104 : */
105 : void setExpertMode(bool checked);
106 : /**
107 : * @brief Mirrors the e-mail field into the template's `Name-Email:` line.
108 : * @param arg1 The new field text.
109 : */
110 : void on_email_textChanged(const QString &arg1);
111 : /**
112 : * @brief Mirrors the name field into the template's `Name-Real:` line.
113 : * @param arg1 The new field text.
114 : */
115 : void on_name_textChanged(const QString &arg1);
116 :
117 : private:
118 : /**
119 : * @brief OK is available when a passphrase is typed twice the same, or
120 : * when no passphrase was asked for explicitly. Empty fields alone are not
121 : * a choice: the key would be stored unprotected without anyone saying so.
122 : */
123 : void updateOkState();
124 : /**
125 : * @brief Whether the passphrase question has an answer: typed twice the
126 : * same, or waived with the checkbox. Empty fields alone are neither.
127 : * @return true when generation may proceed as far as the passphrase goes.
128 : */
129 : auto passphraseChosen() const -> bool;
130 : /** @brief Generated form (`keygendialog.ui`); owned. */
131 : QScopedPointer<Ui::KeygenDialog> ui;
132 : /**
133 : * @brief Rewrites every `<key>: ...` line of the batch template to carry
134 : * @p value. Values that fail isBatchValue() are ignored so a pasted line
135 : * break can never become a directive of its own.
136 : * @param key Batch parameter name, e.g. `Name-Real`.
137 : * @param value New value for that parameter.
138 : */
139 : void replace(const QString &key, const QString &value);
140 : /**
141 : * @brief On `Accepted`, validates the form (passphrase chosen, no line
142 : * breaks, name of 5+ characters, plausible e-mail), swaps the form for the
143 : * progress indicator and calls startGeneration(); the dialog only closes
144 : * once the backend reports success. Any other result disconnects the
145 : * backend signals and closes normally.
146 : * @param r The QDialog result code.
147 : */
148 : void done(int r) override;
149 : /**
150 : * @brief Connects to the backend's finished/failed signals for this one
151 : * attempt and calls Pass::GenerateGPGKeys(). With no backend the attempt
152 : * fails at once.
153 : * @param batch Complete batch text, passphrase already spliced in.
154 : */
155 : void startGeneration(const QString &batch);
156 : /**
157 : * @brief Stops and hides the progress indicator, re-enables the form under
158 : * the usual OK rule and shows @p error in the status label, so the user can
159 : * correct the input and try again.
160 : * @param error Backend error text; HTML-escaped before display.
161 : */
162 : void generationFailed(const QString &error);
163 : /** @brief Backend that runs gpg; not owned, may be nullptr. */
164 : Pass *m_pass;
165 : /** @brief Connection to Pass::finishedGenerateGPGKeys, live only during an
166 : * attempt. */
167 : QMetaObject::Connection m_finished;
168 : /** @brief Connection to Pass::generateGPGKeysFailed, live only during an
169 : * attempt. */
170 : QMetaObject::Connection m_failed;
171 : /** @brief Spinner shown while gpg works; created lazily on the first
172 : * attempt, reused (hidden) across retries. */
173 : std::unique_ptr<QProgressIndicator> m_progressIndicator;
174 : };
175 :
176 : #endif // SRC_KEYGENDIALOG_H_
|