1 #if defined(HAVE_LIBSECRET)
2 #include <libsecret/secret.h>
3 #endif
4
5 #include "libsecret_p.h"
6
7 #include <QLibrary>
8
9 #if defined(HAVE_LIBSECRET)
qtkeychainSchema(void)10 const SecretSchema* qtkeychainSchema(void) {
11 static const SecretSchema schema = {
12 "org.qt.keychain", SECRET_SCHEMA_DONT_MATCH_NAME,
13 {
14 { "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
15 { "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
16 { "type", SECRET_SCHEMA_ATTRIBUTE_STRING }
17 }
18 };
19
20 return &schema;
21 }
22
23 typedef struct {
24 QKeychain::JobPrivate *self;
25 QString user;
26 QString server;
27 } callbackArg;
28
29 typedef void (*secret_password_lookup_t) (const SecretSchema *schema,
30 GCancellable *cancellable,
31 GAsyncReadyCallback callback,
32 gpointer user_data,
33 ...) G_GNUC_NULL_TERMINATED;
34 typedef gchar *(*secret_password_lookup_finish_t) (GAsyncResult *result,
35 GError **error);
36 typedef void (*secret_password_store_t) (const SecretSchema *schema,
37 const gchar *collection,
38 const gchar *label,
39 const gchar *password,
40 GCancellable *cancellable,
41 GAsyncReadyCallback callback,
42 gpointer user_data,
43 ...) G_GNUC_NULL_TERMINATED;
44 typedef gboolean (*secret_password_store_finish_t) (GAsyncResult *result,
45 GError **error);
46 typedef void (*secret_password_clear_t) (const SecretSchema *schema,
47 GCancellable *cancellable,
48 GAsyncReadyCallback callback,
49 gpointer user_data,
50 ...) G_GNUC_NULL_TERMINATED;
51 typedef gboolean (*secret_password_clear_finish_t) (GAsyncResult *result,
52 GError **error);
53 typedef void (*secret_password_free_t) (gchar *password);
54 typedef GQuark (*secret_error_get_quark_t) (void) G_GNUC_CONST;
55
56 static secret_password_lookup_t secret_password_lookup_fn = nullptr;
57 static secret_password_lookup_finish_t secret_password_lookup_finish_fn = nullptr;
58 static secret_password_store_t secret_password_store_fn = nullptr;
59 static secret_password_store_finish_t secret_password_store_finish_fn = nullptr;
60 static secret_password_clear_t secret_password_clear_fn = nullptr;
61 static secret_password_clear_finish_t secret_password_clear_finish_fn = nullptr;
62 static secret_password_free_t secret_password_free_fn = nullptr;
63 static secret_error_get_quark_t secret_error_get_quark_fn = nullptr;
64
gerrorToCode(const GError * error)65 static QKeychain::Error gerrorToCode(const GError *error) {
66 if (error->domain != secret_error_get_quark_fn()) {
67 return QKeychain::OtherError;
68 }
69
70 switch(error->code) {
71 case SECRET_ERROR_NO_SUCH_OBJECT:
72 return QKeychain::EntryNotFound;
73 case SECRET_ERROR_IS_LOCKED:
74 return QKeychain::AccessDenied;
75 default:
76 return QKeychain::OtherError;
77 }
78 }
79
80 static void
on_password_lookup(GObject * source,GAsyncResult * result,gpointer inst)81 on_password_lookup (GObject *source,
82 GAsyncResult *result,
83 gpointer inst)
84 {
85 GError *error = nullptr;
86 callbackArg *arg = (callbackArg*)inst;
87 gchar *password = secret_password_lookup_finish_fn (result, &error);
88
89 Q_UNUSED(source);
90
91 if (arg) {
92 if (error) {
93 QKeychain::Error code = gerrorToCode(error);
94
95 arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) );
96 } else {
97 if (password) {
98 QByteArray raw = QByteArray(password);
99 switch(arg->self->mode) {
100 case QKeychain::JobPrivate::Binary:
101 arg->self->data = QByteArray::fromBase64(raw);
102 break;
103 case QKeychain::JobPrivate::Text:
104 default:
105 arg->self->data = raw;
106 }
107
108 arg->self->q->emitFinished();
109 } else if (arg->self->mode == QKeychain::JobPrivate::Text) {
110 arg->self->mode = QKeychain::JobPrivate::Binary;
111 secret_password_lookup_fn (qtkeychainSchema(), nullptr,
112 on_password_lookup, arg,
113 "user", arg->user.toUtf8().constData(),
114 "server", arg->server.toUtf8().constData(),
115 "type", "base64",
116 nullptr);
117 return;
118 } else {
119 arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") );
120 }
121 }
122 }
123 if (error) {
124 g_error_free (error);
125 }
126
127 if (password) {
128 secret_password_free_fn (password);
129 }
130
131 if (arg) {
132 delete arg;
133 }
134 }
135
136 static void
on_password_stored(GObject * source,GAsyncResult * result,gpointer inst)137 on_password_stored (GObject *source,
138 GAsyncResult *result,
139 gpointer inst)
140 {
141 GError *error = nullptr;
142 QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
143
144 Q_UNUSED(source);
145
146 secret_password_store_finish_fn (result, &error);
147
148 if (self) {
149 if (error) {
150 self->q->emitFinishedWithError( gerrorToCode(error),
151 QString::fromUtf8(error->message) );
152 } else {
153 self->q->emitFinished();
154 }
155 }
156 if (error) {
157 g_error_free (error);
158 }
159 }
160
161 static void
on_password_cleared(GObject * source,GAsyncResult * result,gpointer inst)162 on_password_cleared (GObject *source,
163 GAsyncResult *result,
164 gpointer inst)
165 {
166 GError *error = nullptr;
167 QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
168 gboolean removed = secret_password_clear_finish_fn (result, &error);
169
170 Q_UNUSED(source);
171 if (self) {
172 if ( error ) {
173 self->q->emitFinishedWithError( gerrorToCode(error),
174 QString::fromUtf8(error->message) );
175 } else {
176 Q_UNUSED(removed);
177 self->q->emitFinished();
178 }
179 }
180 if (error) {
181 g_error_free (error);
182 }
183 }
184
modeToString(QKeychain::JobPrivate::Mode mode)185 static QString modeToString(QKeychain::JobPrivate::Mode mode) {
186 switch(mode) {
187 case QKeychain::JobPrivate::Binary:
188 return "base64";
189 default:
190 return "plaintext";
191 }
192 }
193 #endif
194
isAvailable()195 bool LibSecretKeyring::isAvailable() {
196 #if defined(HAVE_LIBSECRET)
197 const LibSecretKeyring& keyring = instance();
198 if (!keyring.isLoaded())
199 return false;
200 if (secret_password_lookup_fn == nullptr)
201 return false;
202 if (secret_password_lookup_finish_fn == nullptr)
203 return false;
204 if (secret_password_store_fn == nullptr)
205 return false;
206 if (secret_password_store_finish_fn == nullptr)
207 return false;
208 if (secret_password_clear_fn == nullptr)
209 return false;
210 if (secret_password_clear_finish_fn == nullptr)
211 return false;
212 if (secret_password_free_fn == nullptr)
213 return false;
214 if (secret_error_get_quark_fn == nullptr)
215 return false;
216 return true;
217 #else
218 return false;
219 #endif
220 }
221
findPassword(const QString & user,const QString & server,QKeychain::JobPrivate * self)222 bool LibSecretKeyring::findPassword(const QString &user, const QString &server,
223 QKeychain::JobPrivate *self)
224 {
225 #if defined(HAVE_LIBSECRET)
226 if (!isAvailable()) {
227 return false;
228 }
229
230 self->mode = QKeychain::JobPrivate::Text;
231 self->data = QByteArray();
232
233 callbackArg *arg = new callbackArg;
234 arg->self = self;
235 arg->user = user;
236 arg->server = server;
237
238 secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg,
239 "user", user.toUtf8().constData(),
240 "server", server.toUtf8().constData(),
241 "type", "plaintext",
242 nullptr);
243 return true;
244 #else
245 Q_UNUSED(user)
246 Q_UNUSED(server)
247 Q_UNUSED(self)
248 return false;
249 #endif
250 }
251
writePassword(const QString & display_name,const QString & user,const QString & server,const QKeychain::JobPrivate::Mode mode,const QByteArray & password,QKeychain::JobPrivate * self)252 bool LibSecretKeyring::writePassword(const QString &display_name,
253 const QString &user,
254 const QString &server,
255 const QKeychain::JobPrivate::Mode mode,
256 const QByteArray &password,
257 QKeychain::JobPrivate *self)
258 {
259 #if defined(HAVE_LIBSECRET)
260 if (!isAvailable()) {
261 return false;
262 }
263
264 QString type = modeToString(mode);
265 QByteArray pwd;
266 switch(mode) {
267 case QKeychain::JobPrivate::Binary:
268 pwd = password.toBase64();
269 break;
270 default:
271 pwd = password;
272 break;
273 }
274
275 secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT,
276 display_name.toUtf8().constData(),
277 pwd.constData(), nullptr, on_password_stored, self,
278 "user", user.toUtf8().constData(),
279 "server", server.toUtf8().constData(),
280 "type", type.toUtf8().constData(),
281 nullptr);
282 return true;
283 #else
284 Q_UNUSED(display_name)
285 Q_UNUSED(user)
286 Q_UNUSED(server)
287 Q_UNUSED(mode)
288 Q_UNUSED(password)
289 Q_UNUSED(self)
290 return false;
291 #endif
292 }
293
deletePassword(const QString & key,const QString & service,QKeychain::JobPrivate * self)294 bool LibSecretKeyring::deletePassword(const QString &key, const QString &service,
295 QKeychain::JobPrivate* self)
296 {
297 #if defined(HAVE_LIBSECRET)
298 if (!isAvailable()) {
299 return false;
300 }
301
302 secret_password_clear_fn (qtkeychainSchema(), nullptr, on_password_cleared, self,
303 "user", key.toUtf8().constData(),
304 "server", service.toUtf8().constData(),
305 nullptr);
306 return true;
307 #else
308 Q_UNUSED(key)
309 Q_UNUSED(service)
310 Q_UNUSED(self)
311 return false;
312 #endif
313 }
314
LibSecretKeyring()315 LibSecretKeyring::LibSecretKeyring()
316 : QLibrary(QLatin1String("secret-1"), 0)
317 {
318 #ifdef HAVE_LIBSECRET
319 if (load()) {
320 secret_password_lookup_fn =
321 (secret_password_lookup_t)resolve("secret_password_lookup");
322 secret_password_lookup_finish_fn =
323 (secret_password_lookup_finish_t)resolve("secret_password_lookup_finish");
324 secret_password_store_fn =
325 (secret_password_store_t)resolve("secret_password_store");
326 secret_password_store_finish_fn =
327 (secret_password_store_finish_t)resolve("secret_password_store_finish");
328 secret_password_clear_fn =
329 (secret_password_clear_t)resolve("secret_password_clear");
330 secret_password_clear_finish_fn =
331 (secret_password_clear_finish_t)resolve("secret_password_clear_finish");
332 secret_password_free_fn =
333 (secret_password_free_t)resolve("secret_password_free");
334 secret_error_get_quark_fn =
335 (secret_error_get_quark_t)resolve("secret_error_get_quark");
336 }
337 #endif
338 }
339
instance()340 LibSecretKeyring &LibSecretKeyring::instance() {
341 static LibSecretKeyring instance;
342
343 return instance;
344 }
345