pidgin/pidgin

Add a get_minimum_search_length to PurpleProtocolContacts
default tip
9 days ago, Gary Kramlich
5ebb4beb29b7
Add a get_minimum_search_length to PurpleProtocolContacts

This can be used by user interfaces, to not call
PurpleProtocolContacts.search_async with strings smaller than this length.

Testing Done:
Called in the turtles and ran the protocol_contacts test under valgrind.

Reviewed at https://reviews.imfreedom.org/r/3164/
/*
* Purple - Internet Messaging Library
* Copyright (C) Pidgin Developers <devel@pidgin.im>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <https://www.gnu.org/licenses/>.
*/
/* TODO
*
* This keyring now works (at the time of this writing), but there are
* some inconvenient edge cases. When looking up passwords, libsecret
* doesn't error if the keyring is locked. Therefore, it appears to
* this plugin that there's no stored password. libpurple seems to
* handle this as gracefully as possible, but it's still inconvenient.
* This plugin could possibly be ported to use libsecret's "Complete API"
* to resolve this if desired.
*/
#include <glib/gi18n-lib.h>
#include <gplugin.h>
#include <gplugin-native.h>
#include <purple.h>
#include <libsecret/secret.h>
/* Translators: Secret Service is a service that runs on the user's computer.
It is one option for where the user's passwords can be stored. It is a
project name. It may not be appropriate to translate this string, but
transliterating to your alphabet is reasonable. More info about the
project can be found at https://wiki.gnome.org/Projects/Libsecret */
#define LIBSECRET_ID "libsecret"
#define LIBSECRET_NAME N_("libsecret")
#define LIBSECRET_DESCRIPTION N_("Credential provider for libsecret. Common " \
"in GNOME and other desktop environments.")
/******************************************************************************
* Globals
*****************************************************************************/
static PurpleCredentialProvider *instance = NULL;
static const SecretSchema purple_libsecret_schema = {
"im.pidgin.Purple3", SECRET_SCHEMA_NONE,
{
{"user", SECRET_SCHEMA_ATTRIBUTE_STRING},
{"protocol", SECRET_SCHEMA_ATTRIBUTE_STRING},
{"NULL", 0}
},
/* Reserved fields */
0, 0, 0, 0, 0, 0, 0, 0
};
#define PURPLE_TYPE_LIBSECRET (purple_libsecret_get_type())
G_DECLARE_FINAL_TYPE(PurpleLibSecret, purple_libsecret,
PURPLE, LIBSECRET, PurpleCredentialProvider)
struct _PurpleLibSecret {
PurpleCredentialProvider parent;
};
G_DEFINE_DYNAMIC_TYPE_EXTENDED(PurpleLibSecret, purple_libsecret,
PURPLE_TYPE_CREDENTIAL_PROVIDER,
G_TYPE_FLAG_FINAL, {})
/******************************************************************************
* Callbacks
*****************************************************************************/
static void
purple_libsecret_read_password_callback(G_GNUC_UNUSED GObject *obj,
GAsyncResult *result, gpointer data)
{
GTask *task = G_TASK(data);
GError *error = NULL;
gchar *password = NULL;
password = secret_password_lookup_finish(result, &error);
if(error != NULL) {
g_task_return_error(task, error);
} else {
g_task_return_pointer(task, password, g_free);
}
g_object_unref(task);
}
static void
purple_libsecret_write_password_callback(G_GNUC_UNUSED GObject *obj,
GAsyncResult *result, gpointer data)
{
GTask *task = G_TASK(data);
GError *error = NULL;
gboolean ret = FALSE;
ret = secret_password_store_finish(result, &error);
if(error != NULL) {
g_task_return_error(task, error);
} else {
g_task_return_boolean(task, ret);
}
g_object_unref(task);
}
static void
purple_libsecret_clear_password_callback(G_GNUC_UNUSED GObject *obj,
GAsyncResult *result, gpointer data)
{
GTask *task = G_TASK(data);
GError *error = NULL;
/* This returns whether a password was removed or not. Which means that it
* can return FALSE with error unset. This would complicate all of the other
* credential API and we don't need to make this distinction, so we just
* return TRUE unless error is set.
*/
secret_password_clear_finish(result, &error);
if(error != NULL) {
g_task_return_error(task, error);
} else {
g_task_return_boolean(task, TRUE);
}
g_object_unref(task);
}
/******************************************************************************
* PurpleCredentialProvider Implementation
*****************************************************************************/
static void
purple_libsecret_read_password_async(PurpleCredentialProvider *provider,
PurpleAccount *account,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer data)
{
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
GTask *task = NULL;
task = g_task_new(provider, cancellable, callback, data);
g_task_set_source_tag(task, purple_libsecret_read_password_async);
secret_password_lookup(&purple_libsecret_schema, cancellable,
purple_libsecret_read_password_callback, task,
"user", purple_contact_info_get_username(info),
"protocol", purple_account_get_protocol_id(account),
NULL);
}
static gchar *
purple_libsecret_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);
}
static void
purple_libsecret_write_password_async(PurpleCredentialProvider *provider,
PurpleAccount *account,
const gchar *password,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer data)
{
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
GTask *task = NULL;
gchar *label = NULL;
const gchar *username = NULL;
task = g_task_new(provider, cancellable, callback, data);
g_task_set_source_tag(task, purple_libsecret_write_password_async);
username = purple_contact_info_get_username(info);
label = g_strdup_printf(_("libpurple password for account %s"), username);
secret_password_store(&purple_libsecret_schema,
SECRET_COLLECTION_DEFAULT, label, password,
cancellable,
purple_libsecret_write_password_callback, task,
"user", username,
"protocol", purple_account_get_protocol_id(account),
NULL);
g_free(label);
}
static gboolean
purple_libsecret_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 void
purple_libsecret_clear_password_async(PurpleCredentialProvider *provider,
PurpleAccount *account,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer data)
{
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
GTask *task = NULL;
task = g_task_new(provider, cancellable, callback, data);
g_task_set_source_tag(task, purple_libsecret_clear_password_async);
secret_password_clear(&purple_libsecret_schema, cancellable,
purple_libsecret_clear_password_callback, task,
"user", purple_contact_info_get_username(info),
"protocol", purple_account_get_protocol_id(account),
NULL);
}
static gboolean
purple_libsecret_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
*****************************************************************************/
static void
purple_libsecret_init(G_GNUC_UNUSED PurpleLibSecret *libsecret) {
}
static void
purple_libsecret_class_init(PurpleLibSecretClass *klass) {
PurpleCredentialProviderClass *provider_class = NULL;
provider_class = PURPLE_CREDENTIAL_PROVIDER_CLASS(klass);
provider_class->read_password_async =
purple_libsecret_read_password_async;
provider_class->read_password_finish =
purple_libsecret_read_password_finish;
provider_class->write_password_async =
purple_libsecret_write_password_async;
provider_class->write_password_finish =
purple_libsecret_write_password_finish;
provider_class->clear_password_async =
purple_libsecret_clear_password_async;
provider_class->clear_password_finish =
purple_libsecret_clear_password_finish;
}
static void
purple_libsecret_class_finalize(G_GNUC_UNUSED PurpleLibSecretClass *klass) {
}
/******************************************************************************
* API
*****************************************************************************/
static PurpleCredentialProvider *
purple_libsecret_new(void) {
return g_object_new(
PURPLE_TYPE_LIBSECRET,
"id", LIBSECRET_ID,
"name", _(LIBSECRET_NAME),
"description", _(LIBSECRET_DESCRIPTION),
NULL
);
}
/******************************************************************************
* Plugin Exports
*****************************************************************************/
static GPluginPluginInfo *
libsecret_query(G_GNUC_UNUSED GError **error) {
const gchar * const authors[] = {
"Pidgin Developers <devel@pidgin.im>",
NULL
};
return GPLUGIN_PLUGIN_INFO(purple_plugin_info_new(
"id", "credential-provider-" LIBSECRET_ID,
"name", LIBSECRET_NAME,
"version", DISPLAY_VERSION,
"category", N_("Credentials"),
"summary", "libsecret credential provider",
"description", N_("Adds support for using libsecret as a credential "
"provider."),
"authors", authors,
"website", PURPLE_WEBSITE,
"abi-version", PURPLE_ABI_VERSION,
"flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
NULL
));
}
static gboolean
libsecret_load(GPluginPlugin *plugin, GError **error) {
PurpleCredentialManager *manager = NULL;
purple_libsecret_register_type(G_TYPE_MODULE(plugin));
manager = purple_credential_manager_get_default();
instance = purple_libsecret_new();
return purple_credential_manager_register(manager, instance, error);
}
static gboolean
libsecret_unload(G_GNUC_UNUSED GPluginPlugin *plugin,
G_GNUC_UNUSED gboolean shutdown,
GError **error)
{
PurpleCredentialManager *manager = NULL;
gboolean ret = FALSE;
manager = purple_credential_manager_get_default();
ret = purple_credential_manager_unregister(manager, instance, error);
if(!ret) {
return ret;
}
g_clear_object(&instance);
return TRUE;
}
GPLUGIN_NATIVE_PLUGIN_DECLARE(libsecret)