2 * @file secretservice.c Secret Service password storage
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
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)"
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
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
},
65 0, 0, 0, 0, 0, 0, 0, 0
68 typedef struct _InfoStorage InfoStorage
;
72 PurpleAccount
*account
;
77 /***********************************************/
78 /* Keyring interface */
79 /***********************************************/
81 ss_read_continue(GObject
*object
, GAsyncResult
*result
, gpointer data
)
83 InfoStorage
*storage
= data
;
84 PurpleAccount
*account
= storage
->account
;
85 PurpleKeyringReadCallback cb
= storage
->cb
;
89 password
= secret_password_lookup_finish(result
, &error
);
92 int code
= error
->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
));
103 cb(account
, NULL
, error
, storage
->user_data
);
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
,
115 error
= g_error_new(PURPLE_KEYRING_ERROR
,
116 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
117 "Unknown error (account : %s).",
118 purple_account_get_username(account
));
120 cb(account
, NULL
, error
, storage
->user_data
);
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
));
131 cb(account
, NULL
, error
, storage
->user_data
);
136 cb(account
, password
, NULL
, storage
->user_data
);
143 ss_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
, gpointer data
)
145 InfoStorage
*storage
= g_new0(InfoStorage
, 1);
147 storage
->account
= account
;
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
);
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
;
167 secret_password_store_finish(result
, &error
);
170 int code
= error
->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
));
185 cb(account
, error
, storage
->user_data
);
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
,
197 error
= g_error_new(PURPLE_KEYRING_ERROR
,
198 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
199 "Unknown error (account : %s).",
200 purple_account_get_username(account
));
202 cb(account
, error
, storage
->user_data
);
208 purple_debug_info("keyring-libsecret", "Password for %s updated.\n",
209 purple_account_get_username(account
));
212 cb(account
, NULL
, storage
->user_data
);
219 ss_save(PurpleAccount
*account
,
220 const gchar
*password
,
221 PurpleKeyringSaveCallback cb
,
224 InfoStorage
*storage
= g_new0(InfoStorage
, 1);
226 storage
->account
= account
;
228 storage
->user_data
= data
;
230 if (password
!= NULL
&& *password
!= '\0') {
231 const char *username
= purple_account_get_username(account
);
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
,
242 "protocol", purple_account_get_protocol_id(account
),
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
),
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
);
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)",
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."),
309 "website", PURPLE_WEBSITE
,
310 "abi-version", PURPLE_ABI_VERSION
,
311 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
,
317 plugin_load(PurplePlugin
*plugin
, GError
**error
)
319 return ss_init(error
);
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 "
336 PURPLE_PLUGIN_INIT(secret_service
, plugin_query
, plugin_load
, plugin_unload
);