--- a/libpurple/plugins/keyrings/wincred.c Fri Jan 29 06:54:08 2021 -0600
+++ b/libpurple/plugins/keyrings/wincred.c Sat Jan 30 00:36:07 2021 -0600
@@ -26,19 +26,32 @@
#define WINCRED_NAME N_("Windows credentials")
-#define WINCRED_SUMMARY N_("Store passwords using Windows credentials")
-#define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \
-#define WINCRED_AUTHORS {"Tomek Wasilczyk <twasilczyk@pidgin.im>", NULL}
#define WINCRED_ID "keyring-wincred"
-#define WINCRED_DOMAIN (g_quark_from_static_string(WINCRED_ID))
#define WINCRED_MAX_TARGET_NAME 256
-static PurpleKeyring *keyring_handler = NULL;
+/****************************************************************************** + *****************************************************************************/ +static PurpleCredentialProvider *instance = NULL; +#define PURPLE_TYPE_WINCRED (purple_wincred_get_type()) +G_DECLARE_FINAL_TYPE(PurpleWinCred, purple_wincred, PURPLE, SECRET_SERVICE, + PurpleCredentialProvider) + PurpleCredentialProvider parent; +G_DEFINE_DYNAMIC_TYPE(PurpleWinCred, purple_wincred, + PURPLE_TYPE_CREDENTIAL_PROVIDER) +/****************************************************************************** + * PurpleCredentialProvider Implementation + *****************************************************************************/ -wincred_get_target_name(PurpleAccount *account)
+wincred_get_target_name(PurpleAccount *account, GError **error) gchar target_name_utf8[WINCRED_MAX_TARGET_NAME];
gunichar2 *target_name_utf16;
@@ -49,30 +62,35 @@
purple_account_get_protocol_id(account),
purple_account_get_username(account));
- target_name_utf16 = g_utf8_to_utf16(target_name_utf8, -1,
+ g_utf8_to_utf16(target_name_utf8, -1, NULL, NULL, error); if (target_name_utf16 == NULL) {
- purple_debug_fatal("keyring-wincred", "Couldn't convert target "
+ purple_debug_fatal("keyring-wincred", "Couldn't convert target name"); return target_name_utf16;
-wincred_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
+purple_wincred_read_password_async(PurpleCredentialProvider *provider, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer data) gunichar2 *target_name = NULL;
- PCREDENTIALW credential;
+ gchar *password = NULL; + PCREDENTIALW credential = NULL; - g_return_if_fail(account != NULL);
- target_name = wincred_get_target_name(account);
- g_return_if_fail(target_name != NULL);
+ task = g_task_new(G_OBJECT(provider), cancellable, callback, data); + target_name = wincred_get_target_name(account, &error); + if (target_name == NULL) { + g_task_return_error(task, error); if (!CredReadW(target_name, CRED_TYPE_GENERIC, 0, &credential)) {
DWORD error_code = GetLastError();
@@ -103,9 +121,7 @@
_("Cannot read password (error %lx)."), error_code);
- cb(account, NULL, error, data);
+ g_task_return_error(task, error); @@ -122,87 +138,66 @@
error = g_error_new(PURPLE_KEYRING_ERROR,
PURPLE_KEYRING_ERROR_BACKENDFAIL,
_("Cannot read password (unicode error)."));
+ g_task_return_error(task, error); purple_debug_misc("keyring-wincred",
_("Got password for account %s.\n"),
purple_account_get_username(account));
- cb(account, password, error, data);
+ g_task_return_pointer(task, password, g_free); - purple_str_wipe(password);
+purple_wincred_read_password_finish(PurpleCredentialProvider *provider, + GAsyncResult *result, GError **error) + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE); + g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE); + return g_task_propagate_pointer(G_TASK(result), error); -wincred_save(PurpleAccount *account, const gchar *password,
- PurpleKeyringSaveCallback cb, gpointer data)
+purple_wincred_write_password_async(PurpleCredentialProvider *provider, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer data) gunichar2 *target_name = NULL;
gunichar2 *username_utf16 = NULL;
gunichar2 *password_utf16 = NULL;
+ glong password_len = 0; - g_return_if_fail(account != NULL);
- target_name = wincred_get_target_name(account);
- g_return_if_fail(target_name != NULL);
- if (password == NULL) {
- if (CredDeleteW(target_name, CRED_TYPE_GENERIC, 0)) {
- purple_debug_misc("keyring-wincred", "Password for "
- "account %s removed\n",
- purple_account_get_username(account));
- DWORD error_code = GetLastError();
+ task = g_task_new(G_OBJECT(provider), cancellable, callback, data); - if (error_code == ERROR_NOT_FOUND) {
- if (purple_debug_is_verbose()) {
- purple_debug_misc("keyring-wincred",
- "Password for account %s was already "
- purple_account_get_username(account));
- } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
- purple_debug_error("keyring-wincred",
- "Cannot remove password, no valid "
- error = g_error_new(PURPLE_KEYRING_ERROR,
- PURPLE_KEYRING_ERROR_ACCESSDENIED,
- _("Cannot remove password, no valid "
- purple_debug_error("keyring-wincred",
- "Cannot remove password, error %lx\n",
- error = g_error_new(PURPLE_KEYRING_ERROR,
- PURPLE_KEYRING_ERROR_BACKENDFAIL,
- _("Cannot remove password (error %lx)."),
- cb(account, error, data);
+ target_name = wincred_get_target_name(account, &error); + if (target_name == NULL) { + g_task_return_error(task, error); - username_utf16 = g_utf8_to_utf16(purple_account_get_username(account),
- password_utf16 = g_utf8_to_utf16(password, -1, NULL, NULL, NULL);
+ username_utf16 = g_utf8_to_utf16(purple_account_get_username(account), -1, + if (username_utf16 == NULL) { + purple_debug_fatal("keyring-wincred", "Couldn't convert username"); + g_task_return_error(task, error); - if (username_utf16 == NULL || password_utf16 == NULL) {
+ password_utf16 = g_utf8_to_utf16(password, -1, NULL, &password_len, &error); + if (password_utf16 == NULL) { - purple_utf16_wipe(password_utf16);
- purple_debug_fatal("keyring-wincred", "Couldn't convert "
- "username or password\n");
+ purple_debug_fatal("keyring-wincred", "Couldn't convert password"); + g_task_return_error(task, error); memset(&credential, 0, sizeof(CREDENTIALW));
@@ -218,12 +213,10 @@
if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
purple_debug_error("keyring-wincred",
- "Cannot store password, no valid logon "
- error = g_error_new(PURPLE_KEYRING_ERROR,
- PURPLE_KEYRING_ERROR_ACCESSDENIED,
- _("Cannot remove password, no valid logon "
+ "Cannot store password, no valid logon session"); + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_ACCESSDENIED, + _("Cannot remove password, no valid logon session.")); purple_debug_error("keyring-wincred",
"Cannot store password, error %lx\n",
@@ -233,72 +226,200 @@
_("Cannot store password (error %lx)."), error_code);
- purple_debug_misc("keyring-wincred",
- "Password updated for account %s.\n",
- purple_account_get_username(account));
+ purple_debug_misc("keyring-wincred", "Password updated for account %s.", + purple_account_get_username(account)); - purple_utf16_wipe(password_utf16);
+ memset(password_utf16, 0, password_len * sizeof(gunichar)); + g_free(password_utf16);
- cb(account, error, data);
+ g_task_return_error(task, error); + g_task_return_boolean(task, TRUE); +purple_wincred_write_password_finish(PurpleCredentialProvider *provider, + GAsyncResult *result, GError **error) + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE); + g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE); + return g_task_propagate_boolean(G_TASK(result), error); -static PurplePluginInfo *
-plugin_query(GError **error)
+purple_wincred_clear_password_async(PurpleCredentialProvider *provider, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer data) - const gchar * const authors[] = WINCRED_AUTHORS;
+ gunichar2 *target_name = NULL; + task = g_task_new(G_OBJECT(provider), cancellable, callback, data); + target_name = wincred_get_target_name(account, &error); + if (target_name == NULL) { + g_task_return_error(task, error); + if (CredDeleteW(target_name, CRED_TYPE_GENERIC, 0)) { + purple_debug_misc("keyring-wincred", "Password for account %s removed", + purple_account_get_username(account)); + g_task_return_boolean(task, TRUE); - return purple_plugin_info_new(
- "version", DISPLAY_VERSION,
- "category", N_("Keyring"),
- "summary", WINCRED_SUMMARY,
- "description", WINCRED_DESCRIPTION,
- "website", PURPLE_WEBSITE,
- "abi-version", PURPLE_ABI_VERSION,
- "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
+ DWORD error_code = GetLastError(); + if (error_code == ERROR_NOT_FOUND) { + if (purple_debug_is_verbose()) { + "Password for account %s was already removed.", + purple_account_get_username(account)); + } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) { + "Cannot remove password, no valid logon session"); + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_ACCESSDENIED, + _("Cannot remove password, no valid logon session.")); + purple_debug_error("keyring-wincred", + "Cannot remove password, error %lx", error_code); + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Cannot remove password (error %lx)."), error_code); + g_task_return_error(task, error); -plugin_load(PurplePlugin *plugin, GError **error)
+purple_wincred_clear_password_finish(PurpleCredentialProvider *provider, + GAsyncResult *result, GError **error) + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE); + g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE); + return g_task_propagate_boolean(G_TASK(result), error); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +purple_wincred_init(PurpleWinCred *wincred) +purple_wincred_class_init(PurpleWinCredClass *klass) - keyring_handler = purple_keyring_new();
+ PurpleCredentialProviderClass *provider_class = NULL; + provider_class = PURPLE_CREDENTIAL_PROVIDER_CLASS(klass); + provider_class->read_password_async = purple_wincred_read_password_async; + provider_class->read_password_finish = purple_wincred_read_password_finish; + provider_class->write_password_async = purple_wincred_write_password_async; + provider_class->write_password_finish = + purple_wincred_write_password_finish; + provider_class->clear_password_async = purple_wincred_clear_password_async; + provider_class->clear_password_finish = + purple_wincred_clear_password_finish; +purple_wincred_class_finalize(PurpleWinCredClass *klass) +/****************************************************************************** + *****************************************************************************/ +static PurpleCredentialProvider * +purple_wincred_new(void) + return PURPLE_CREDENTIAL_PROVIDER(g_object_new( + "name", _(WINCRED_NAME), +/****************************************************************************** + *****************************************************************************/ - purple_keyring_set_name(keyring_handler, _(WINCRED_NAME));
- purple_keyring_set_id(keyring_handler, WINCRED_ID);
- purple_keyring_set_read_password(keyring_handler, wincred_read);
- purple_keyring_set_save_password(keyring_handler, wincred_save);
+G_MODULE_EXPORT GPluginPluginInfo *gplugin_query(GError **error); +G_MODULE_EXPORT gboolean gplugin_load(GPluginNativePlugin *plugin, +G_MODULE_EXPORT gboolean gplugin_unload(GPluginNativePlugin *plugin, +G_MODULE_EXPORT GPluginPluginInfo * +gplugin_query(GError **error) + const gchar * const authors[] = { + "Tomek Wasilczyk <twasilczyk@pidgin.im>", + return GPLUGIN_PLUGIN_INFO(purple_plugin_info_new( + "version", DISPLAY_VERSION, + "category", _("Keyring"), + "summary", _("Store passwords using Windows credentials"), + "description", _("This plugin stores passwords using Windows credentials."), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, - purple_keyring_register(keyring_handler);
+G_MODULE_EXPORT gboolean +gplugin_load(GPluginNativePlugin *plugin, GError **error) + PurpleCredentialManager *manager = NULL; + purple_wincred_register_type(G_TYPE_MODULE(plugin)); + manager = purple_credential_manager_get_default(); + instance = purple_wincred_new(); + return purple_credential_manager_register_provider(manager, instance, +G_MODULE_EXPORT gboolean +gplugin_unload(GPluginNativePlugin *plugin, GError **error) + PurpleCredentialManager *manager = NULL; + manager = purple_credential_manager_get_default(); + ret = purple_credential_manager_unregister_provider(manager, instance, + g_clear_object(&instance);
-plugin_unload(PurplePlugin *plugin, GError **error)
- if (purple_keyring_get_inuse() == keyring_handler) {
- g_set_error(error, WINCRED_DOMAIN, 0, "The keyring is currently "
- purple_debug_warning("keyring-wincred",
- "keyring in use, cannot unload\n");
- purple_keyring_unregister(keyring_handler);
- purple_keyring_free(keyring_handler);
- keyring_handler = NULL;
-PURPLE_PLUGIN_INIT(wincred_keyring, plugin_query, plugin_load, plugin_unload);