Line data Source code
1 : // SPDX-FileCopyrightText: 2026 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : /**
4 : * @file
5 : * @brief ClipboardManager, which puts secrets on the clipboard and removes
6 : * them again, and the MIME data that marks them as secrets.
7 : */
8 : #ifndef SRC_CLIPBOARDMANAGER_H_
9 : #define SRC_CLIPBOARDMANAGER_H_
10 :
11 : #include <QMimeData>
12 : #include <QObject>
13 : #include <QString>
14 : #include <QStringList>
15 : #include <QTimer>
16 : #include <optional>
17 :
18 : /**
19 : * @brief Build clipboard MIME data with platform-specific security hints.
20 : * @param text - Plain text to copy
21 : * @return QMimeData* - Ownership transferred to caller. Caller must delete
22 : * or transfer to QClipboard::setMimeData which takes ownership.
23 : */
24 : auto buildClipboardMimeData(const QString &text) -> QMimeData *;
25 :
26 : /**
27 : * @brief Convert quint32 to byte array for Windows clipboard formats.
28 : * @param value - DWORD value
29 : * @return QByteArray with raw bytes
30 : */
31 : static inline auto dwordBytes(quint32 value) -> QByteArray {
32 : return {reinterpret_cast<const char *>(&value), sizeof(value)};
33 : }
34 :
35 : /**
36 : * @class ClipboardManager
37 : * @ingroup secrets
38 : * @brief Puts secrets on the system clipboard and takes them off again.
39 : *
40 : * Remembers what it copied so the autoclear timer (and the quit handler)
41 : * only remove that text, never something the user copied in the meantime.
42 : * Settings (selection vs. clipboard, autoclear on/off, delay) are read from
43 : * QtPassSettings; call setAutoclearTimer() after they change. Status text
44 : * for the main window's status bar is emitted, not shown, so this class has
45 : * no UI dependency and is unit-testable.
46 : */
47 : class ClipboardManager : public QObject {
48 50 : Q_OBJECT
49 :
50 : public:
51 : /**
52 : * @brief Construct a manager that also clears its text when the
53 : * application quits.
54 : * @param parent Owner.
55 : */
56 : explicit ClipboardManager(QObject *parent = nullptr);
57 :
58 : /**
59 : * @brief Re-read the autoclear delay from the settings.
60 : */
61 : void setAutoclearTimer();
62 :
63 : /**
64 : * @brief Handle clipboard copying for a shown entry.
65 : *
66 : * In "always copy" mode (and only then) this copies @p text via copyText(),
67 : * which also arms the autoclear timer and records the clipboard contents.
68 : * In other modes it does nothing, leaving any pending autoclear tracking
69 : * intact.
70 : * @param text Password (or value) of the entry being shown.
71 : * @param p_output Full decrypted output; copying is skipped when it is empty.
72 : */
73 : void copyIfAlways(const QString &text, const QString &p_output = QString());
74 :
75 : /**
76 : * @brief The text this manager last put on the clipboard and still tracks.
77 : * @return Tracked text, empty once cleared.
78 : */
79 : [[nodiscard]] auto trackedText() const -> QString { return m_clippedText; }
80 :
81 : public slots:
82 : /**
83 : * @brief Copy text into the system clipboard (or the X11 primary selection
84 : * when configured) and arm the autoclear timer when enabled.
85 : * @param text Text to copy.
86 : */
87 : void copyText(const QString &text);
88 :
89 : /**
90 : * @brief Track text that got onto the clipboard some other way.
91 : *
92 : * For a copy QtPass did not make through copyText(): text selected and
93 : * copied from a widget showing a secret (Ctrl+C, the context menu, a drag,
94 : * or the X11 primary selection that a mouse selection fills). The text is
95 : * cleared with the rest when the autoclear timer runs; what copyText()
96 : * tracks stays tracked too.
97 : * @param text The copied text; nothing happens when it is empty.
98 : */
99 : void trackCopied(const QString &text);
100 :
101 : /**
102 : * @brief Remove the tracked text from the clipboard if it is still there.
103 : * Silent no-op when nothing is tracked.
104 : */
105 : void clear();
106 :
107 : signals:
108 : /**
109 : * @brief Something worth showing in the status bar happened.
110 : * @param message Translated text.
111 : */
112 : void statusMessage(const QString &message);
113 :
114 : private:
115 : /**
116 : * @brief Single-shot autoclear timer; started by copyText() when the
117 : * autoclear setting is on, and calls clear() when it fires.
118 : */
119 : QTimer m_clearTimer;
120 : /**
121 : * @brief The secret copyText() last put on the clipboard; clear() removes
122 : * the clipboard content only while it still equals this.
123 : */
124 : QString m_clippedText;
125 : /// What was on the clipboard and in the selection before copyText() first
126 : /// put a secret there, for clear() to put back (restoreClipboard). Text
127 : /// only, as pass -c keeps it; absent when nothing was saved.
128 : std::optional<QString> m_previousClipboard;
129 : /** @brief As m_previousClipboard, for the X11 primary selection. */
130 : std::optional<QString> m_previousSelection;
131 : /**
132 : * @brief Empty a saved previous text equal to @p text, now copied as a
133 : * secret, so clear() does not put the secret back.
134 : * @param text The text that has just been copied as a secret.
135 : */
136 : void forgetIfSaved(const QString &text);
137 : QStringList m_otherCopies; ///< trackCopied() texts, cleared with the rest
138 : };
139 :
140 : #endif // SRC_CLIPBOARDMANAGER_H_
|