Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / plugins / keyrings / secretservice.c
blob3227c7f1881982e842ed2a270a48865699e65212
1 /* purple
2 * @file secretservice.c Secret Service password storage
3 * @ingroup plugins
5 * Purple is the legal property of its developers, whose names are too numerous
6 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * source distribution.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program ; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 /* TODO
26 * This keyring now works (at the time of this writing), but there are
27 * some inconvenient edge cases. When looking up passwords, libsecret
28 * doesn't error if the keyring is locked. Therefore, it appears to
29 * this plugin that there's no stored password. libpurple seems to
30 * handle this as gracefully as possible, but it's still inconvenient.
31 * This plugin could possibly be ported to use libsecret's "Complete API"
32 * to resolve this if desired.
35 #include "internal.h"
36 #include <purple.h>
38 #include <libsecret/secret.h>
40 /* Translators: Secret Service is a service that runs on the user's computer.
41 It is one option for where the user's passwords can be stored. It is a
42 project name. It may not be appropriate to translate this string, but
43 transliterating to your alphabet is reasonable. More info about the
44 project can be found at https://wiki.gnome.org/Projects/Libsecret */
45 #define SECRETSERVICE_NAME N_("Secret Service")
46 #define SECRETSERVICE_ID "keyring-libsecret"
47 #define SECRETSERVICE_DOMAIN (g_quark_from_static_string(SECRETSERVICE_ID))
49 static PurpleKeyring *keyring_handler = NULL;
50 static GCancellable *keyring_cancellable = NULL;
52 static const SecretSchema purple_schema = {
53 "im.pidgin.Purple", SECRET_SCHEMA_NONE,
55 {"user", SECRET_SCHEMA_ATTRIBUTE_STRING},
56 {"protocol", SECRET_SCHEMA_ATTRIBUTE_STRING},
57 {"NULL", 0}
59 /* Reserved fields */
60 0, 0, 0, 0, 0, 0, 0, 0
63 typedef struct _InfoStorage InfoStorage;
65 struct _InfoStorage
67 PurpleAccount *account;
68 gpointer cb;
69 gpointer user_data;
72 /***********************************************/
73 /* Keyring interface */
74 /***********************************************/
75 static void
76 ss_g_error_to_keyring_error(GError **error, PurpleAccount *account)
78 GError *old_error;
79 GError *new_error = NULL;
81 g_return_if_fail(error != NULL);
83 old_error = *error;
85 if (g_error_matches(old_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
86 new_error = g_error_new_literal(PURPLE_KEYRING_ERROR,
87 PURPLE_KEYRING_ERROR_CANCELLED,
88 _("Operation cancelled."));
89 } else if (g_error_matches(old_error, G_DBUS_ERROR,
90 G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND) ||
91 g_error_matches(old_error, G_DBUS_ERROR,
92 G_DBUS_ERROR_IO_ERROR)) {
93 purple_debug_info("keyring-libsecret",
94 "Failed to communicate with Secret "
95 "Service (account: %s (%s)).\n",
96 purple_account_get_username(account),
97 purple_account_get_protocol_id(account));
98 new_error = g_error_new(PURPLE_KEYRING_ERROR,
99 PURPLE_KEYRING_ERROR_BACKENDFAIL,
100 "Failed to communicate with Secret "
101 "Service (account: %s).",
102 purple_account_get_username(account));
103 } else if (g_error_matches(old_error, SECRET_ERROR,
104 SECRET_ERROR_IS_LOCKED)) {
105 purple_debug_info("keyring-libsecret",
106 "Secret Service is locked (account: %s (%s)).\n",
107 purple_account_get_username(account),
108 purple_account_get_protocol_id(account));
109 new_error = g_error_new(PURPLE_KEYRING_ERROR,
110 PURPLE_KEYRING_ERROR_ACCESSDENIED,
111 "Secret Service is locked (account: %s).",
112 purple_account_get_username(account));
113 } else {
114 purple_debug_error("keyring-libsecret",
115 "Unknown error (account: %s (%s), "
116 "domain: %s, code: %d): %s.\n",
117 purple_account_get_username(account),
118 purple_account_get_protocol_id(account),
119 g_quark_to_string(old_error->domain),
120 old_error->code, old_error->message);
121 new_error = g_error_new(PURPLE_KEYRING_ERROR,
122 PURPLE_KEYRING_ERROR_BACKENDFAIL,
123 "Unknown error (account: %s).",
124 purple_account_get_username(account));
127 g_clear_error(error);
128 g_propagate_error(error, new_error);
131 static void
132 ss_read_continue(GObject *object, GAsyncResult *result, gpointer data)
134 InfoStorage *storage = data;
135 PurpleAccount *account = storage->account;
136 PurpleKeyringReadCallback cb = storage->cb;
137 char *password;
138 GError *error = NULL;
140 password = secret_password_lookup_finish(result, &error);
142 if (error != NULL) {
143 ss_g_error_to_keyring_error(&error, account);
144 } else if (password == NULL) {
145 error = g_error_new(PURPLE_KEYRING_ERROR,
146 PURPLE_KEYRING_ERROR_NOPASSWORD,
147 "No password found for account: %s",
148 purple_account_get_username(account));
151 if (cb != NULL) {
152 cb(account, password, error, storage->user_data);
155 g_clear_error(&error);
156 g_free(storage);
159 static void
160 ss_read(PurpleAccount *account, PurpleKeyringReadCallback cb, gpointer data)
162 InfoStorage *storage = g_new0(InfoStorage, 1);
164 storage->account = account;
165 storage->cb = cb;
166 storage->user_data = data;
168 secret_password_lookup(&purple_schema, keyring_cancellable,
169 ss_read_continue, storage,
170 "user", purple_account_get_username(account),
171 "protocol", purple_account_get_protocol_id(account), NULL);
174 static void
175 ss_save_continue(GError *error, gpointer data)
177 InfoStorage *storage = data;
178 PurpleKeyringSaveCallback cb;
179 PurpleAccount *account;
181 account = storage->account;
182 cb = storage->cb;
184 if (error != NULL) {
185 ss_g_error_to_keyring_error(&error, account);
186 } else {
187 purple_debug_info("keyring-libsecret", "Password for %s updated.\n",
188 purple_account_get_username(account));
191 if (cb != NULL)
192 cb(account, error, storage->user_data);
194 g_clear_error(&error);
195 g_free(storage);
198 static void
199 ss_store_continue(GObject *object, GAsyncResult *result, gpointer data)
201 GError *error = NULL;
203 secret_password_store_finish(result, &error);
205 ss_save_continue(error, data);
208 static void
209 ss_clear_continue(GObject *object, GAsyncResult *result, gpointer data)
211 GError *error = NULL;
213 secret_password_clear_finish(result, &error);
215 ss_save_continue(error, data);
218 static void
219 ss_save(PurpleAccount *account,
220 const gchar *password,
221 PurpleKeyringSaveCallback cb,
222 gpointer data)
224 InfoStorage *storage = g_new0(InfoStorage, 1);
226 storage->account = account;
227 storage->cb = cb;
228 storage->user_data = data;
230 if (password != NULL && *password != '\0') {
231 const char *username = purple_account_get_username(account);
232 char *label;
234 purple_debug_info("keyring-libsecret",
235 "Updating password for account %s (%s).\n",
236 username, purple_account_get_protocol_id(account));
238 label = g_strdup_printf(_("Pidgin IM password for account %s"), username);
239 secret_password_store(&purple_schema, SECRET_COLLECTION_DEFAULT,
240 label, password, keyring_cancellable,
241 ss_store_continue, storage,
242 "user", username,
243 "protocol", purple_account_get_protocol_id(account),
244 NULL);
245 g_free(label);
247 } else { /* password == NULL, delete password. */
248 purple_debug_info("keyring-libsecret",
249 "Forgetting password for account %s (%s).\n",
250 purple_account_get_username(account),
251 purple_account_get_protocol_id(account));
253 secret_password_clear(&purple_schema, keyring_cancellable,
254 ss_clear_continue, storage,
255 "user", purple_account_get_username(account),
256 "protocol", purple_account_get_protocol_id(account),
257 NULL);
261 static void
262 ss_cancel(void)
264 g_cancellable_cancel(keyring_cancellable);
266 /* Swap out cancelled cancellable for new one for further operations */
267 g_clear_object(&keyring_cancellable);
268 keyring_cancellable = g_cancellable_new();
271 static void
272 ss_close(void)
274 ss_cancel();
277 static gboolean
278 ss_init(GError **error)
280 keyring_cancellable = g_cancellable_new();
282 keyring_handler = purple_keyring_new();
284 purple_keyring_set_name(keyring_handler, _(SECRETSERVICE_NAME));
285 purple_keyring_set_id(keyring_handler, SECRETSERVICE_ID);
286 purple_keyring_set_read_password(keyring_handler, ss_read);
287 purple_keyring_set_save_password(keyring_handler, ss_save);
288 purple_keyring_set_cancel_requests(keyring_handler, ss_cancel);
289 purple_keyring_set_close_keyring(keyring_handler, ss_close);
291 purple_keyring_register(keyring_handler);
293 return TRUE;
296 static void
297 ss_uninit(void)
299 ss_close();
300 purple_keyring_unregister(keyring_handler);
301 purple_keyring_free(keyring_handler);
302 keyring_handler = NULL;
304 g_clear_object(&keyring_cancellable);
307 /***********************************************/
308 /* Plugin interface */
309 /***********************************************/
311 static PurplePluginInfo *
312 plugin_query(GError **error)
314 const gchar * const authors[] = {
315 "Elliott Sales de Andrade (qulogic[at]pidgin.im)",
316 NULL
319 return purple_plugin_info_new(
320 "id", SECRETSERVICE_ID,
321 "name", SECRETSERVICE_NAME,
322 "version", DISPLAY_VERSION,
323 "category", N_("Keyring"),
324 "summary", "Secret Service Plugin",
325 "description", N_("This plugin will store passwords in Secret Service."),
326 "authors", authors,
327 "website", PURPLE_WEBSITE,
328 "abi-version", PURPLE_ABI_VERSION,
329 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
330 NULL
334 static gboolean
335 plugin_load(PurplePlugin *plugin, GError **error)
337 return ss_init(error);
340 static gboolean
341 plugin_unload(PurplePlugin *plugin, GError **error)
343 if (purple_keyring_get_inuse() == keyring_handler) {
344 g_set_error(error, SECRETSERVICE_DOMAIN, 0, "The keyring is currently "
345 "in use.");
346 return FALSE;
349 ss_uninit();
351 return TRUE;
354 PURPLE_PLUGIN_INIT(secret_service, plugin_query, plugin_load, plugin_unload);