Adapt migration for files
[pidgin-git.git] / libpurple / plugins / keyrings / secretservice.c
blob36f15344e5a315975b6bab5e342f1a675fb1412c
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 #error "This keyring needs some more work (see TODO)"
26 /* TODO
28 * This keyring needs some more work, so it will be disabled until its quality
29 * was raised. Some of the pain points:
30 * - throws a lot of g_warnings
31 * - it doesn't notify about some backend faults (like access denied), some of
32 * them are not handled at all
33 * - it could use libsecret's Complete API
34 * - code formatting could be better
37 #include "internal.h"
38 #include "account.h"
39 #include "debug.h"
40 #include "keyring.h"
41 #include "plugins.h"
42 #include "version.h"
44 #include <libsecret/secret.h>
46 /* Translators: Secret Service is a service that runs on the user's computer.
47 It is one option for where the user's passwords can be stored. It is a
48 project name. It may not be appropriate to translate this string, but
49 transliterating to your alphabet is reasonable. More info about the
50 project can be found at https://wiki.gnome.org/Projects/Libsecret */
51 #define SECRETSERVICE_NAME N_("Secret Service")
52 #define SECRETSERVICE_ID "keyring-libsecret"
53 #define SECRETSERVICE_DOMAIN (g_quark_from_static_string(SECRETSERVICE_ID))
55 static PurpleKeyring *keyring_handler = NULL;
57 static const SecretSchema purple_schema = {
58 "im.pidgin.Purple", SECRET_SCHEMA_NONE,
60 {"user", SECRET_SCHEMA_ATTRIBUTE_STRING},
61 {"protocol", SECRET_SCHEMA_ATTRIBUTE_STRING},
62 {"NULL", 0}
64 /* Reserved fields */
65 0, 0, 0, 0, 0, 0, 0, 0
68 typedef struct _InfoStorage InfoStorage;
70 struct _InfoStorage
72 PurpleAccount *account;
73 gpointer cb;
74 gpointer user_data;
77 /***********************************************/
78 /* Keyring interface */
79 /***********************************************/
80 static void
81 ss_read_continue(GObject *object, GAsyncResult *result, gpointer data)
83 InfoStorage *storage = data;
84 PurpleAccount *account = storage->account;
85 PurpleKeyringReadCallback cb = storage->cb;
86 char *password;
87 GError *error = NULL;
89 password = secret_password_lookup_finish(result, &error);
91 if (error != NULL) {
92 int code = error->code;
94 switch (code) {
95 case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
96 case G_DBUS_ERROR_IO_ERROR:
97 error = g_error_new(PURPLE_KEYRING_ERROR,
98 PURPLE_KEYRING_ERROR_BACKENDFAIL,
99 "Failed to communicate with Secret "
100 "Service (account : %s).",
101 purple_account_get_username(account));
102 if (cb != NULL)
103 cb(account, NULL, error, storage->user_data);
104 g_error_free(error);
105 break;
107 default:
108 purple_debug_error("keyring-libsecret",
109 "Unknown error (account: %s (%s), "
110 "domain: %s, code: %d): %s.\n",
111 purple_account_get_username(account),
112 purple_account_get_protocol_id(account),
113 g_quark_to_string(error->domain), code,
114 error->message);
115 error = g_error_new(PURPLE_KEYRING_ERROR,
116 PURPLE_KEYRING_ERROR_BACKENDFAIL,
117 "Unknown error (account : %s).",
118 purple_account_get_username(account));
119 if (cb != NULL)
120 cb(account, NULL, error, storage->user_data);
121 g_error_free(error);
122 break;
125 } else if (password == NULL) {
126 error = g_error_new(PURPLE_KEYRING_ERROR,
127 PURPLE_KEYRING_ERROR_NOPASSWORD,
128 "No password found for account: %s",
129 purple_account_get_username(account));
130 if (cb != NULL)
131 cb(account, NULL, error, storage->user_data);
132 g_error_free(error);
134 } else {
135 if (cb != NULL)
136 cb(account, password, NULL, storage->user_data);
139 g_free(storage);
142 static void
143 ss_read(PurpleAccount *account, PurpleKeyringReadCallback cb, gpointer data)
145 InfoStorage *storage = g_new0(InfoStorage, 1);
147 storage->account = account;
148 storage->cb = cb;
149 storage->user_data = data;
151 secret_password_lookup(&purple_schema, NULL, ss_read_continue, storage,
152 "user", purple_account_get_username(account),
153 "protocol", purple_account_get_protocol_id(account), NULL);
156 static void
157 ss_save_continue(GObject *object, GAsyncResult *result, gpointer data)
159 InfoStorage *storage = data;
160 PurpleKeyringSaveCallback cb;
161 GError *error = NULL;
162 PurpleAccount *account;
164 account = storage->account;
165 cb = storage->cb;
167 secret_password_store_finish(result, &error);
169 if (error != NULL) {
170 int code = error->code;
172 switch (code) {
173 case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
174 case G_DBUS_ERROR_IO_ERROR:
175 purple_debug_info("keyring-libsecret",
176 "Failed to communicate with Secret "
177 "Service (account : %s (%s)).\n",
178 purple_account_get_username(account),
179 purple_account_get_protocol_id(account));
180 error = g_error_new(PURPLE_KEYRING_ERROR,
181 PURPLE_KEYRING_ERROR_BACKENDFAIL,
182 "Failed to communicate with Secret Service (account : %s).",
183 purple_account_get_username(account));
184 if (cb != NULL)
185 cb(account, error, storage->user_data);
186 g_error_free(error);
187 break;
189 default:
190 purple_debug_error("keyring-libsecret",
191 "Unknown error (account: %s (%s), "
192 "domain: %s, code: %d): %s.\n",
193 purple_account_get_username(account),
194 purple_account_get_protocol_id(account),
195 g_quark_to_string(error->domain), code,
196 error->message);
197 error = g_error_new(PURPLE_KEYRING_ERROR,
198 PURPLE_KEYRING_ERROR_BACKENDFAIL,
199 "Unknown error (account : %s).",
200 purple_account_get_username(account));
201 if (cb != NULL)
202 cb(account, error, storage->user_data);
203 g_error_free(error);
204 break;
207 } else {
208 purple_debug_info("keyring-libsecret", "Password for %s updated.\n",
209 purple_account_get_username(account));
211 if (cb != NULL)
212 cb(account, NULL, storage->user_data);
215 g_free(storage);
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, NULL, ss_save_continue, storage,
241 "user", username,
242 "protocol", purple_account_get_protocol_id(account),
243 NULL);
244 g_free(label);
246 } else { /* password == NULL, delete password. */
247 purple_debug_info("keyring-libsecret",
248 "Forgetting password for account %s (%s).\n",
249 purple_account_get_username(account),
250 purple_account_get_protocol_id(account));
252 secret_password_clear(&purple_schema, NULL, ss_save_continue,
253 storage, "user", purple_account_get_username(account),
254 "protocol", purple_account_get_protocol_id(account),
255 NULL);
259 static void
260 ss_close(void)
264 static gboolean
265 ss_init(GError **error)
267 keyring_handler = purple_keyring_new();
269 purple_keyring_set_name(keyring_handler, _(SECRETSERVICE_NAME));
270 purple_keyring_set_id(keyring_handler, SECRETSERVICE_ID);
271 purple_keyring_set_read_password(keyring_handler, ss_read);
272 purple_keyring_set_save_password(keyring_handler, ss_save);
273 purple_keyring_set_close_keyring(keyring_handler, ss_close);
275 purple_keyring_register(keyring_handler);
277 return TRUE;
280 static void
281 ss_uninit(void)
283 ss_close();
284 purple_keyring_unregister(keyring_handler);
285 purple_keyring_free(keyring_handler);
286 keyring_handler = NULL;
289 /***********************************************/
290 /* Plugin interface */
291 /***********************************************/
293 static PurplePluginInfo *
294 plugin_query(GError **error)
296 const gchar * const authors[] = {
297 "Elliott Sales de Andrade (qulogic[at]pidgin.im)",
298 NULL
301 return purple_plugin_info_new(
302 "id", SECRETSERVICE_ID,
303 "name", SECRETSERVICE_NAME,
304 "version", DISPLAY_VERSION,
305 "category", N_("Keyring"),
306 "summary", "Secret Service Plugin",
307 "description", N_("This plugin will store passwords in Secret Service."),
308 "authors", authors,
309 "website", PURPLE_WEBSITE,
310 "abi-version", PURPLE_ABI_VERSION,
311 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
312 NULL
316 static gboolean
317 plugin_load(PurplePlugin *plugin, GError **error)
319 return ss_init(error);
322 static gboolean
323 plugin_unload(PurplePlugin *plugin, GError **error)
325 if (purple_keyring_get_inuse() == keyring_handler) {
326 g_set_error(error, SECRETSERVICE_DOMAIN, 0, "The keyring is currently "
327 "in use.");
328 return FALSE;
331 ss_uninit();
333 return TRUE;
336 PURPLE_PLUGIN_INIT(secret_service, plugin_query, plugin_load, plugin_unload);