pidgin/pidgin

Parents 86e0c5080a58
Children 141508c66433
Add PurpleCredentialProvider as the first piece to replace the existing keyring api.

Testing Done:
Added some new unit tests and made sure they passs.

Reviewed at https://reviews.imfreedom.org/r/144/
--- a/doc/reference/libpurple/libpurple-docs.xml Thu Oct 08 23:31:08 2020 -0500
+++ b/doc/reference/libpurple/libpurple-docs.xml Thu Oct 08 23:37:43 2020 -0500
@@ -69,6 +69,7 @@
<xi:include href="xml/purpleaccountusersplit.xml" />
<xi:include href="xml/purplebuddypresence.xml" />
<xi:include href="xml/purplechatuser.xml" />
+ <xi:include href="xml/purplecredentialprovider.xml" />
<xi:include href="xml/purpleimconversation.xml" />
<xi:include href="xml/purplekeyvaluepair.xml" />
<xi:include href="xml/purpleprotocolfactory.xml" />
--- a/libpurple/meson.build Thu Oct 08 23:31:08 2020 -0500
+++ b/libpurple/meson.build Thu Oct 08 23:37:43 2020 -0500
@@ -51,10 +51,11 @@
'purpleaccountoption.c',
'purpleaccountpresence.c',
'purpleaccountusersplit.c',
+ 'purpleattachment.c',
'purplebuddypresence.c',
'purplechatuser.c',
+ 'purplecredentialprovider.c',
'purpleimconversation.c',
- 'purpleattachment.c',
'purplekeyvaluepair.c',
'purpleprotocolfactory.c',
'purpleprotocolim.c',
@@ -136,6 +137,7 @@
'purpleaccountusersplit.h',
'purplebuddypresence.h',
'purplechatuser.h',
+ 'purplecredentialprovider.h',
'purpleimconversation.h',
'purpleattachment.h',
'purplekeyvaluepair.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplecredentialprovider.c Thu Oct 08 23:37:43 2020 -0500
@@ -0,0 +1,388 @@
+/*
+ * purple
+ *
+ * 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/>.
+ */
+
+#include "purplecredentialprovider.h"
+
+typedef struct {
+ gchar *id;
+ gchar *name;
+} PurpleCredentialProviderPrivate;
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_NAME,
+ N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {NULL, };
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PurpleCredentialProvider,
+ purple_credential_provider, G_TYPE_OBJECT)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+purple_credential_provider_set_id(PurpleCredentialProvider *provider,
+ const gchar *id)
+{
+ PurpleCredentialProviderPrivate *priv = NULL;
+
+ priv = purple_credential_provider_get_instance_private(provider);
+
+ g_free(priv->id);
+ priv->id = g_strdup(id);
+
+ g_object_notify_by_pspec(G_OBJECT(provider), properties[PROP_ID]);
+}
+
+static void
+purple_credential_provider_set_name(PurpleCredentialProvider *provider,
+ const gchar *name)
+{
+ PurpleCredentialProviderPrivate *priv = NULL;
+
+ priv = purple_credential_provider_get_instance_private(provider);
+
+ g_free(priv->name);
+ priv->name = g_strdup(name);
+
+ g_object_notify_by_pspec(G_OBJECT(provider), properties[PROP_NAME]);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_credential_provider_get_property(GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+
+ switch(param_id) {
+ case PROP_ID:
+ g_value_set_string(value,
+ purple_credential_provider_get_id(provider));
+ break;
+ case PROP_NAME:
+ g_value_set_string(value,
+ purple_credential_provider_get_name(provider));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_credential_provider_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+
+ switch(param_id) {
+ case PROP_ID:
+ purple_credential_provider_set_id(provider,
+ g_value_get_string(value));
+ break;
+ case PROP_NAME:
+ purple_credential_provider_set_name(provider,
+ g_value_get_string(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_credential_provider_finalize(GObject *obj) {
+ PurpleCredentialProvider *provider = NULL;
+ PurpleCredentialProviderPrivate *priv = NULL;
+
+ provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+ priv = purple_credential_provider_get_instance_private(provider);
+
+ g_clear_pointer(&priv->id, g_free);
+ g_clear_pointer(&priv->name, g_free);
+
+ G_OBJECT_CLASS(purple_credential_provider_parent_class)->finalize(obj);
+}
+
+static void
+purple_credential_provider_init(PurpleCredentialProvider *provider) {
+}
+
+static void
+purple_credential_provider_class_init(PurpleCredentialProviderClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ obj_class->get_property = purple_credential_provider_get_property;
+ obj_class->set_property = purple_credential_provider_set_property;
+ obj_class->finalize = purple_credential_provider_finalize;
+
+ /**
+ * PurpleCredentialProvider::id:
+ *
+ * The ID of the provider. Used for preferences and other things that need
+ * to address it.
+ */
+ properties[PROP_ID] = g_param_spec_string(
+ "id", "id", "The identifier of the provider",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
+ );
+
+ /**
+ * PurpleCredentialProvider::name:
+ *
+ * The name of the provider which will be displayed to the user.
+ */
+ properties[PROP_NAME] = g_param_spec_string(
+ "name", "name", "The name of the provider",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
+ );
+
+ g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+const gchar *
+purple_credential_provider_get_id(PurpleCredentialProvider *provider) {
+ PurpleCredentialProviderPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), NULL);
+
+ priv = purple_credential_provider_get_instance_private(provider);
+
+ return priv->id;
+}
+
+const gchar *
+purple_credential_provider_get_name(PurpleCredentialProvider *provider) {
+ PurpleCredentialProviderPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), NULL);
+
+ priv = purple_credential_provider_get_instance_private(provider);
+
+ return priv->name;
+}
+
+gboolean
+purple_credential_provider_is_valid(PurpleCredentialProvider *provider,
+ GError **error)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE);
+
+ if(purple_credential_provider_get_id(provider) == NULL) {
+ g_set_error_literal(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 0,
+ "provider has no id");
+
+ return FALSE;
+ }
+
+ if(purple_credential_provider_get_name(provider) == NULL) {
+ g_set_error_literal(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 1,
+ "provider has no name");
+
+ return FALSE;
+ }
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+
+ if(klass->read_password_async == NULL || klass->read_password_finish == NULL) {
+ g_set_error_literal(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 2,
+ "provider can not read passwords");
+
+ return FALSE;
+ }
+
+ if(klass->write_password_async == NULL || klass->write_password_finish == NULL) {
+ g_set_error_literal(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 3,
+ "provider can not write passwords");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+purple_credential_provider_read_password_async(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider));
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->read_password_async) {
+ klass->read_password_async(provider, account, cancellable, callback,
+ data);
+ }
+}
+
+gchar *
+purple_credential_provider_read_password_finish(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), NULL);
+ g_return_val_if_fail(G_IS_ASYNC_RESULT(result), NULL);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->read_password_finish) {
+ return klass->read_password_finish(provider, account, result, error);
+ }
+
+ return NULL;
+}
+
+void
+purple_credential_provider_write_password_async(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ const gchar *password,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider));
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->write_password_async) {
+ klass->write_password_async(provider, account, password, cancellable,
+ callback, data);
+ }
+}
+
+gboolean
+purple_credential_provider_write_password_finish(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+ g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->write_password_finish) {
+ return klass->write_password_finish(provider, account, result, error);
+ }
+
+ return FALSE;
+}
+
+void
+purple_credential_provider_clear_password_async(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider));
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->clear_password_async) {
+ klass->clear_password_async(provider, account, cancellable, callback,
+ data);
+ }
+}
+
+gboolean
+purple_credential_provider_clear_password_finish(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+ g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->clear_password_finish) {
+ return klass->clear_password_finish(provider, account, result, error);
+ }
+
+ return FALSE;
+}
+
+void
+purple_credential_provider_close(PurpleCredentialProvider *provider) {
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider));
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->close) {
+ klass->close(provider);
+ }
+}
+
+PurpleRequestFields *
+purple_credential_provider_read_settings(PurpleCredentialProvider *provider) {
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), NULL);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->read_settings) {
+ return klass->read_settings(provider);
+ }
+
+ return NULL;
+}
+
+gboolean
+purple_credential_provider_write_settings(PurpleCredentialProvider *provider,
+ PurpleRequestFields *fields)
+{
+ PurpleCredentialProviderClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE);
+ g_return_val_if_fail(fields != NULL, FALSE);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ if(klass && klass->write_settings) {
+ return klass->write_settings(provider, fields);
+ }
+
+ return FALSE;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplecredentialprovider.h Thu Oct 08 23:37:43 2020 -0500
@@ -0,0 +1,278 @@
+/*
+ * purple
+ *
+ * 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/>.
+ */
+
+#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <purple.h> may be included directly"
+#endif
+
+#ifndef PURPLE_CREDENTIAL_PROVIDER_H
+#define PURPLE_CREDENTIAL_PROVIDER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "account.h"
+#include "request.h"
+
+/**
+ * SECTION:purplecredentialprovider
+ * @section_id: libpurple-purplecredentialprovider
+ * @title: Credential Provider Object
+ */
+
+G_BEGIN_DECLS
+
+/**
+ * PURPLE_CREDENTIAL_PROVIDER_DOMAIN:
+ *
+ * A #GError domain for errors from #PurpleCredentialProviders.
+ *
+ * Since: 3.0.0
+ */
+#define PURPLE_CREDENTIAL_PROVIDER_DOMAIN (g_quark_from_static_string("purple-credential-provider"))
+
+/**
+ * PurpleCredentialProvider:
+ *
+ * #PurpleCredentialProvider is a base class that should be sub classed by
+ * credential providers. It defines the behavior of all credential providers
+ * and implements some shared properties.
+ *
+ * Since: 3.0.0
+ */
+
+#define PURPLE_TYPE_CREDENTIAL_PROVIDER (purple_credential_provider_get_type())
+G_DECLARE_DERIVABLE_TYPE(PurpleCredentialProvider, purple_credential_provider,
+ PURPLE, CREDENTIAL_PROVIDER, GObject);
+
+/**
+ * PurpleCredentialProviderClass:
+ * @read_password_async: Reads a password from the provider.
+ * @read_password_finish: Finishes reading a password.
+ * @write_password_async: Writes a password to the provider.
+ * @write_password_finish: Finishes writing a password.
+ * @clear_password_async: Clears a password from the provider.
+ * @clear_password_finish: Finishes clearing a password from the provider.
+ * @close: Closes the provider.
+ * @read_settings: Creates a #PurpleRequestFields for the available settings.
+ * @write_settings: Updates the settings for provider.
+ *
+ * #PurpleCredentialProviderClass defines the interface for interacting with
+ * credential providers like libsecret, kwallet, etc.
+ *
+ * Since: 3.0.0
+ */
+struct _PurpleCredentialProviderClass {
+ /*< private >*/
+ GObjectClass parent;
+
+ /*< public >*/
+ void (*read_password_async)(PurpleCredentialProvider *provider, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+ gchar *(*read_password_finish)(PurpleCredentialProvider *provider, PurpleAccount *account, GAsyncResult *result, GError **error);
+
+ void (*write_password_async)(PurpleCredentialProvider *provider, PurpleAccount *account, const gchar *password, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+ gboolean (*write_password_finish)(PurpleCredentialProvider *provider, PurpleAccount *account, GAsyncResult *result, GError **error);
+
+ void (*clear_password_async)(PurpleCredentialProvider *provider, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+ gboolean (*clear_password_finish)(PurpleCredentialProvider *provider, PurpleAccount *account, GAsyncResult *result, GError **error);
+
+ void (*close)(PurpleCredentialProvider *provider);
+
+ PurpleRequestFields *(*read_settings)(PurpleCredentialProvider *provider);
+ gboolean (*write_settings)(PurpleCredentialProvider *provider, PurpleRequestFields *fields);
+
+ /*< private >*/
+
+ /* Some extra padding to play it safe. */
+ gpointer reserved[8];
+};
+
+/**
+ * purple_credential_provider_get_id:
+ * @provider: The #PurpleCredentialProvider instance.
+ *
+ * Gets the identifier of @provider.
+ *
+ * Returns: The identifier of @provider.
+ *
+ * Since: 3.0.0
+ */
+const gchar *purple_credential_provider_get_id(PurpleCredentialProvider *provider);
+
+/**
+ * purple_credential_provider_get_name:
+ * @provider: The #PurpleCredentialProvider instance.
+ *
+ * Gets the name of @provider which can be show in user interfaces.
+ *
+ * Returns: The name of @provider.
+ *
+ * Since: 3.0.0
+ */
+const gchar *purple_credential_provider_get_name(PurpleCredentialProvider *provider);
+
+/**
+ * purple_credential_provider_is_valid:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @error: (out) (optional): A return address for a #GError.
+ *
+ * Checks whether or not @provider is setup correctly. This is primarily meant
+ * for #purple_keyring_register to call to avoid programming errors, but can be
+ * used by anyone.
+ *
+ * Returns: %FALSE on error, otherwise %TRUE.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_credential_provider_is_valid(PurpleCredentialProvider *provider, GError **error);
+
+/**
+ * purple_credential_provider_read_password_async:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @account: The #PurpleAccount whose password to read.
+ * @cancellable: (nullable): optional GCancellable object, NULL to ignore.
+ * @callback: (scope async): a GAsyncReadyCallback to call when the request is satisfied.
+ * @data: User data to pass to @callback.
+ *
+ * Reads the password for @account from @provider.
+ *
+ * Since: 3.0.0
+ */
+void purple_credential_provider_read_password_async(PurpleCredentialProvider *provider, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+
+/**
+ * purple_credential_provider_read_password_finish:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @account: The #PurpleAccount whose password we're looking up.
+ * @result: The #GAsyncResult from the previous
+ * purple_credential_provider_read_password() call.
+ * @error: (out) (optional): Return address for a #GError.
+ *
+ * Finishes a previous call to purple_credential_provider_read_password().
+ *
+ * Returns: (transfer full): The password or %NULL if successful, otherwise
+ * %NULL with @error set on failure.
+ *
+ * Since: 3.0.0
+ */
+gchar *purple_credential_provider_read_password_finish(PurpleCredentialProvider *provider, PurpleAccount *account, GAsyncResult *result, GError **error);
+
+/**
+ * purple_credential_provider_write_password_async:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @account: The #PurpleAccount whose password to write.
+ * @password: The password to write.
+ * @cancellable: (nullable): optional GCancellable object, NULL to ignore.
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is
+ * satisfied.
+ * @data: User data to pass to @callback.
+ *
+ * Writes @password for @account to @provider.
+ *
+ * Since: 3.0.0
+ */
+void purple_credential_provider_write_password_async(PurpleCredentialProvider *provider, PurpleAccount *account, const gchar *password, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+
+/**
+ * purple_credential_provider_write_password_finish:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @account: The #PurpleAccount whose password we're writing.
+ * @result: The #GAsyncResult from the previous
+ * purple_credential_provider_write_password() call.
+ * @error: (out) (optional): Return address for a #GError.
+ *
+ * Finishes a previous call to purple_credential_provider_write_password().
+ *
+ * Returns: %TRUE if the password was written successfully, otherwise %FALSE
+ * with @error set.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_credential_provider_write_password_finish(PurpleCredentialProvider *provider, PurpleAccount *account, GAsyncResult *result, GError **error);
+
+/**
+ * purple_credential_provider_clear_password_async:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @account: The #PurpleAccount whose password to clear.
+ * @cancellable: (nullable): optional #GCancellable object, or %NULL to ignore.
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is
+ * satisfied.
+ * @data: User data to pass to @callback.
+ *
+ * Clears the password for @account from @provider.
+ *
+ * Since: 3.0.0
+ */
+void purple_credential_provider_clear_password_async(PurpleCredentialProvider *provider, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+
+/**
+ * purple_credential_provider_clear_password_finish:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @account: The #PurpleAccount whose password we're clearing.
+ * @result: The #GAsyncResult from the previous
+ * purple_credential_provider_clear_password() call.
+ * @error: (out) (optional): Return address for a #GError.
+ *
+ * Finishes a previous call to purple_credential_provider_clear_password().
+ *
+ * Returns: %TRUE if the password was cleared successfully, otherwise %FALSE
+ * with @error set.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_credential_provider_clear_password_finish(PurpleCredentialProvider *provider, PurpleAccount *account, GAsyncResult *result, GError **error);
+
+/**
+ * purple_credential_provider_close:
+ * @provider: The #PurpleCredentialProvider instance.
+ *
+ * Tells @provider to close. This is useful if you need to disconnect a socket
+ * or close a file to save memory.
+ *
+ * Since: 3.0.0
+ */
+void purple_credential_provider_close(PurpleCredentialProvider *provider);
+
+/**
+ * purple_credential_provider_read_settings:
+ * @provider: The #PurpleCredentialProviderInstance.
+ *
+ * Reads settings from @provider.
+ *
+ * Returns: (transfer full): New copy of current settings which must be free'd
+ * with purple_request_fields_destroy().
+ *
+ * Since: 3.0.0
+ */
+PurpleRequestFields *purple_credential_provider_read_settings(PurpleCredentialProvider *provider);
+
+/**
+ * purple_credential_provider_write_settings:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @fields: Modified settings from purple_credential_provider_read_settings().
+ *
+ * Write @fields to @provider.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_credential_provider_write_settings(PurpleCredentialProvider *provider, PurpleRequestFields *fields);
+
+G_END_DECLS
+
+#endif /* PURPLE_CREDENTIAL_PROVIDER */
--- a/libpurple/tests/meson.build Thu Oct 08 23:31:08 2020 -0500
+++ b/libpurple/tests/meson.build Thu Oct 08 23:37:43 2020 -0500
@@ -2,6 +2,7 @@
'account_option',
'attention_type',
'circular_buffer',
+ 'credential_provider',
'image',
'keyvaluepair',
'protocol_action',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_credential_provider.c Thu Oct 08 23:37:43 2020 -0500
@@ -0,0 +1,656 @@
+/*
+ * purple
+ *
+ * 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/>.
+ */
+
+#include <glib.h>
+
+#include <purple.h>
+
+#include "test_ui.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+/* Since we're using GTask to test asynchrous functions, we need to use a main
+ * loop.
+ */
+static GMainLoop *loop = FALSE;
+
+/******************************************************************************
+ * TestCredentialProviderEmpty
+ *****************************************************************************/
+#define TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER_EMPTY (test_purple_credential_provider_empty_get_type())
+G_DECLARE_FINAL_TYPE(TestPurpleCredentialProviderEmpty,
+ test_purple_credential_provider_empty,
+ TEST_PURPLE, CREDENTIAL_PROVIDER_EMPTY,
+ PurpleCredentialProvider)
+
+struct _TestPurpleCredentialProviderEmpty {
+ PurpleCredentialProvider parent;
+};
+
+G_DEFINE_TYPE(TestPurpleCredentialProviderEmpty,
+ test_purple_credential_provider_empty,
+ PURPLE_TYPE_CREDENTIAL_PROVIDER)
+
+static void
+test_purple_credential_provider_empty_read_password_async(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+}
+
+static gchar *
+test_purple_credential_provider_empty_read_password_finish(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ return NULL;
+}
+
+static void
+test_purple_credential_provider_empty_write_password_async(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ const gchar *password,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+}
+
+static gboolean
+test_purple_credential_provider_empty_write_password_finish(PurpleCredentialProvider *provider,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ return TRUE;
+}
+
+static void
+test_purple_credential_provider_empty_class_init(TestPurpleCredentialProviderEmptyClass *klass)
+{
+}
+
+static void
+test_purple_credential_provider_empty_init(TestPurpleCredentialProviderEmpty *provider)
+{
+}
+
+/******************************************************************************
+ * purple_credential_provider_is_valid tests
+ *****************************************************************************/
+static void
+test_purple_credential_provider_is_valid_no_id(void) {
+ PurpleCredentialProvider *provider = NULL;
+ GError *error = NULL;
+
+ provider = g_object_new(
+ TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER_EMPTY,
+ "name", "name",
+ NULL);
+
+ g_assert_false(purple_credential_provider_is_valid(provider, &error));
+ g_assert_error(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 0);
+ g_clear_error(&error);
+
+ g_object_unref(G_OBJECT(provider));
+}
+
+static void
+test_purple_credential_provider_is_valid_no_name(void) {
+ PurpleCredentialProvider *provider = NULL;
+ GError *error = NULL;
+
+ provider = g_object_new(
+ TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER_EMPTY,
+ "id", "id",
+ NULL);
+
+ g_assert_false(purple_credential_provider_is_valid(provider, &error));
+ g_assert_error(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 1);
+ g_clear_error(&error);
+
+ g_object_unref(G_OBJECT(provider));
+}
+
+static void
+test_purple_credential_provider_is_valid_no_reader(void) {
+ PurpleCredentialProvider *provider = NULL;
+ PurpleCredentialProviderClass *klass = NULL;
+ GError *error = NULL;
+
+ provider = g_object_new(
+ TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER_EMPTY,
+ "id", "id",
+ "name", "name",
+ NULL);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ klass->read_password_async = NULL;
+ klass->read_password_finish = NULL;
+ klass->write_password_async = test_purple_credential_provider_empty_write_password_async;
+ klass->write_password_finish = test_purple_credential_provider_empty_write_password_finish;
+
+ g_assert_false(purple_credential_provider_is_valid(provider, &error));
+ g_assert_error(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 2);
+ g_clear_error(&error);
+
+ g_object_unref(G_OBJECT(provider));
+}
+
+static void
+test_purple_credential_provider_is_valid_no_writer(void) {
+ PurpleCredentialProvider *provider = NULL;
+ PurpleCredentialProviderClass *klass = NULL;
+ GError *error = NULL;
+
+ provider = g_object_new(
+ TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER_EMPTY,
+ "id", "id",
+ "name", "name",
+ NULL);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ klass->read_password_async = test_purple_credential_provider_empty_read_password_async;
+ klass->read_password_finish = test_purple_credential_provider_empty_read_password_finish;
+ klass->write_password_async = NULL;
+ klass->write_password_finish = NULL;
+
+ g_assert_false(purple_credential_provider_is_valid(provider, &error));
+ g_assert_error(error, PURPLE_CREDENTIAL_PROVIDER_DOMAIN, 3);
+ g_clear_error(&error);
+
+ g_object_unref(G_OBJECT(provider));
+}
+
+static void
+test_purple_credential_provider_is_valid_valid(void) {
+ PurpleCredentialProvider *provider = NULL;
+ PurpleCredentialProviderClass *klass = NULL;
+ GError *error = NULL;
+
+ provider = g_object_new(
+ TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER_EMPTY,
+ "id", "id",
+ "name", "name",
+ NULL);
+
+ klass = PURPLE_CREDENTIAL_PROVIDER_GET_CLASS(provider);
+ klass->read_password_async = test_purple_credential_provider_empty_read_password_async;
+ klass->read_password_finish = test_purple_credential_provider_empty_read_password_finish;
+ klass->write_password_async = test_purple_credential_provider_empty_write_password_async;
+ klass->write_password_finish = test_purple_credential_provider_empty_write_password_finish;
+
+ g_assert_true(purple_credential_provider_is_valid(provider, &error));
+ g_assert_no_error(error);
+
+ g_object_unref(G_OBJECT(provider));
+}
+
+/******************************************************************************
+ * TestPurpleCredentialProvider
+ *****************************************************************************/
+#define TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER (test_purple_credential_provider_get_type())
+G_DECLARE_FINAL_TYPE(TestPurpleCredentialProvider,
+ test_purple_credential_provider,
+ TEST_PURPLE, CREDENTIAL_PROVIDER,
+ PurpleCredentialProvider)
+
+struct _TestPurpleCredentialProvider {
+ PurpleCredentialProvider parent;
+
+ gboolean read_password_async;
+ gboolean read_password_finish;
+ gboolean write_password_async;
+ gboolean write_password_finish;
+ gboolean clear_password_async;
+ gboolean clear_password_finish;
+ gboolean close;
+ gboolean read_settings;
+ gboolean write_settings;
+};
+
+G_DEFINE_TYPE(TestPurpleCredentialProvider,
+ test_purple_credential_provider,
+ PURPLE_TYPE_CREDENTIAL_PROVIDER)
+
+static void
+test_purple_credential_provider_read_password_async(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+ GTask *task = NULL;
+
+ provider->read_password_async = TRUE;
+
+ task = g_task_new(p, cancellable, callback, data);
+ g_task_return_pointer(task, NULL, NULL);
+ g_object_unref(G_OBJECT(task));
+}
+
+static gchar *
+test_purple_credential_provider_read_password_finish(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ provider->read_password_finish = TRUE;
+
+ return NULL;
+}
+
+static void
+test_purple_credential_provider_write_password_async(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ const gchar *password,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+ GTask *task = NULL;
+
+ provider->write_password_async = TRUE;
+
+ task = g_task_new(p, cancellable, callback, data);
+ g_task_return_boolean(task, TRUE);
+ g_object_unref(G_OBJECT(task));
+}
+
+static gboolean
+test_purple_credential_provider_write_password_finish(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ provider->write_password_finish = TRUE;
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_provider_clear_password_async(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+ GTask *task = NULL;
+
+ provider->clear_password_async = TRUE;
+
+ task = g_task_new(p, cancellable, callback, data);
+ g_task_return_boolean(task, TRUE);
+ g_object_unref(G_OBJECT(task));
+}
+
+static gboolean
+test_purple_credential_provider_clear_password_finish(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ provider->clear_password_finish = TRUE;
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_provider_close(PurpleCredentialProvider *p) {
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ provider->close = TRUE;
+}
+
+static PurpleRequestFields *
+test_purple_credential_provider_read_settings(PurpleCredentialProvider *p) {
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ provider->read_settings = TRUE;
+
+ return NULL;
+}
+
+static gboolean
+test_purple_credential_provider_write_settings(PurpleCredentialProvider *p,
+ PurpleRequestFields *fields)
+{
+ TestPurpleCredentialProvider *provider = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ provider->write_settings = TRUE;
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_provider_init(TestPurpleCredentialProvider *provider) {
+}
+
+static void
+test_purple_credential_provider_class_init(TestPurpleCredentialProviderClass *klass)
+{
+ PurpleCredentialProviderClass *provider_class = PURPLE_CREDENTIAL_PROVIDER_CLASS(klass);
+
+ provider_class->read_password_async = test_purple_credential_provider_read_password_async;
+ provider_class->read_password_finish = test_purple_credential_provider_read_password_finish;
+ provider_class->write_password_async = test_purple_credential_provider_write_password_async;
+ provider_class->write_password_finish = test_purple_credential_provider_write_password_finish;
+ provider_class->clear_password_async = test_purple_credential_provider_clear_password_async;
+ provider_class->clear_password_finish = test_purple_credential_provider_clear_password_finish;
+ provider_class->close = test_purple_credential_provider_close;
+ provider_class->read_settings = test_purple_credential_provider_read_settings;
+ provider_class->write_settings = test_purple_credential_provider_write_settings;
+}
+
+static PurpleCredentialProvider *
+test_purple_credential_provider_new(void) {
+ return g_object_new(
+ TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER,
+ "id", "test-provider",
+ "name", "Test Provider",
+ NULL);
+}
+
+/******************************************************************************
+ * TestPurpleCredentialProvider Tests
+ *****************************************************************************/
+static gboolean
+test_purple_credential_provider_timeout_cb(gpointer data) {
+ g_main_loop_quit((GMainLoop *)data);
+
+ g_warning("timed out waiting for the callback function to be called");
+}
+
+static void
+test_purple_credential_provider_test_read_cb(GObject *obj, GAsyncResult *res,
+ gpointer d)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+ gchar *password = NULL;
+
+ password = purple_credential_provider_read_password_finish(provider,
+ account, res,
+ NULL);
+
+ g_object_unref(G_OBJECT(account));
+
+ g_main_loop_quit(loop);
+
+ g_assert_null(password);
+}
+
+static gboolean
+test_purple_credential_provider_test_read_idle(gpointer data) {
+ PurpleCredentialProvider *p = PURPLE_CREDENTIAL_PROVIDER(data);
+ PurpleAccount *account = NULL;
+
+ account = purple_account_new("test", "test");
+
+ purple_credential_provider_read_password_async(p, account, NULL,
+ test_purple_credential_provider_test_read_cb,
+ account);
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_provider_test_read(void) {
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ g_idle_add(test_purple_credential_provider_test_read_idle, p);
+ g_timeout_add_seconds(10, test_purple_credential_provider_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+
+ g_assert_true(tp->read_password_async);
+ g_assert_true(tp->read_password_finish);
+ g_assert_false(tp->write_password_async);
+ g_assert_false(tp->write_password_finish);
+ g_assert_false(tp->clear_password_async);
+ g_assert_false(tp->clear_password_finish);
+ g_assert_false(tp->close);
+ g_assert_false(tp->read_settings);
+ g_assert_false(tp->write_settings);
+
+ g_object_unref(p);
+}
+
+static void
+test_purple_credential_provider_test_write_cb(GObject *obj, GAsyncResult *res,
+ gpointer d)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+
+ test_purple_credential_provider_write_password_finish(provider, account,
+ res, NULL);
+
+ g_object_unref(G_OBJECT(account));
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+test_purple_credential_provider_test_write_idle(gpointer data) {
+ PurpleCredentialProvider *p = PURPLE_CREDENTIAL_PROVIDER(data);
+ PurpleAccount *account = NULL;
+
+ account = purple_account_new("test", "test");
+
+ purple_credential_provider_write_password_async(p, account, NULL, NULL,
+ test_purple_credential_provider_test_write_cb,
+ account);
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_provider_test_write(void) {
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ g_idle_add(test_purple_credential_provider_test_write_idle, p);
+ g_timeout_add_seconds(10, test_purple_credential_provider_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+
+ g_assert_false(tp->read_password_async);
+ g_assert_false(tp->read_password_finish);
+ g_assert_true(tp->write_password_async);
+ g_assert_true(tp->write_password_finish);
+ g_assert_false(tp->clear_password_async);
+ g_assert_false(tp->clear_password_finish);
+ g_assert_false(tp->close);
+ g_assert_false(tp->read_settings);
+ g_assert_false(tp->write_settings);
+
+ g_object_unref(G_OBJECT(p));
+}
+
+static void
+test_purple_credential_provider_test_clear_cb(GObject *obj, GAsyncResult *res,
+ gpointer d)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+
+ test_purple_credential_provider_clear_password_finish(provider, account,
+ res, NULL);
+
+ g_object_unref(G_OBJECT(account));
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+test_purple_credential_provider_test_clear_idle(gpointer data) {
+ PurpleCredentialProvider *p = PURPLE_CREDENTIAL_PROVIDER(data);
+ PurpleAccount *account = NULL;
+
+ account = purple_account_new("test", "test");
+
+ purple_credential_provider_clear_password_async(p, account, NULL,
+ test_purple_credential_provider_test_clear_cb,
+ account);
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_provider_test_clear(void) {
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ g_idle_add(test_purple_credential_provider_test_clear_idle, p);
+ g_timeout_add_seconds(10, test_purple_credential_provider_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+
+ g_assert_false(tp->read_password_async);
+ g_assert_false(tp->read_password_finish);
+ g_assert_false(tp->write_password_async);
+ g_assert_false(tp->write_password_finish);
+ g_assert_true(tp->clear_password_async);
+ g_assert_true(tp->clear_password_finish);
+ g_assert_false(tp->close);
+ g_assert_false(tp->read_settings);
+ g_assert_false(tp->write_settings);
+
+ g_object_unref(p);
+}
+
+static void
+test_purple_credential_provider_test_close(void) {
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ purple_credential_provider_close(p);
+
+ g_assert_false(tp->read_password_async);
+ g_assert_false(tp->read_password_finish);
+ g_assert_false(tp->write_password_async);
+ g_assert_false(tp->write_password_finish);
+ g_assert_false(tp->clear_password_async);
+ g_assert_false(tp->clear_password_finish);
+ g_assert_true(tp->close);
+ g_assert_false(tp->read_settings);
+ g_assert_false(tp->write_settings);
+
+ g_object_unref(G_OBJECT(p));
+}
+
+static void
+test_purple_credential_provider_test_read_settings(void) {
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ purple_credential_provider_read_settings(p);
+
+ g_assert_false(tp->read_password_async);
+ g_assert_false(tp->read_password_finish);
+ g_assert_false(tp->write_password_async);
+ g_assert_false(tp->write_password_finish);
+ g_assert_false(tp->clear_password_async);
+ g_assert_false(tp->clear_password_finish);
+ g_assert_false(tp->close);
+ g_assert_true(tp->read_settings);
+ g_assert_false(tp->write_settings);
+
+ g_object_unref(G_OBJECT(p));
+}
+
+static void
+test_purple_credential_provider_test_write_settings(void) {
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+ PurpleRequestFields *fields = purple_request_fields_new();
+
+ purple_credential_provider_write_settings(p, fields);
+ purple_request_fields_destroy(fields);
+
+ g_assert_false(tp->read_password_async);
+ g_assert_false(tp->read_password_finish);
+ g_assert_false(tp->write_password_async);
+ g_assert_false(tp->write_password_finish);
+ g_assert_false(tp->clear_password_async);
+ g_assert_false(tp->clear_password_finish);
+ g_assert_false(tp->close);
+ g_assert_false(tp->read_settings);
+ g_assert_true(tp->write_settings);
+
+ g_object_unref(G_OBJECT(p));
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar *argv[]) {
+ gint ret = 0;
+
+ g_test_init(&argc, &argv, NULL);
+
+ test_ui_purple_init();
+
+ loop = g_main_loop_new(NULL, FALSE);
+
+ g_test_add_func("/credential-provider/is-valid/no-id",
+ test_purple_credential_provider_is_valid_no_id);
+ g_test_add_func("/credential-provider/is-valid/no-name",
+ test_purple_credential_provider_is_valid_no_name);
+ g_test_add_func("/credential-provider/is-valid/no-reader",
+ test_purple_credential_provider_is_valid_no_reader);
+ g_test_add_func("/credential-provider/is-valid/no-writer",
+ test_purple_credential_provider_is_valid_no_writer);
+ g_test_add_func("/credential-provider/is-valid/valid",
+ test_purple_credential_provider_is_valid_valid);
+
+ g_test_add_func("/credential-provider/functional/read",
+ test_purple_credential_provider_test_read);
+ g_test_add_func("/credential-provider/functional/write",
+ test_purple_credential_provider_test_write);
+ g_test_add_func("/credential-provider/functional/clear",
+ test_purple_credential_provider_test_clear);
+ g_test_add_func("/credential-provider/functional/close",
+ test_purple_credential_provider_test_close);
+ g_test_add_func("/credential-provider/functional/read_settings",
+ test_purple_credential_provider_test_read_settings);
+ g_test_add_func("/credential-provider/functional/write_settings",
+ test_purple_credential_provider_test_write_settings);
+
+ ret = g_test_run();
+
+ g_main_loop_unref(loop);
+
+ return ret;
+}
--- a/po/POTFILES.in Thu Oct 08 23:31:08 2020 -0500
+++ b/po/POTFILES.in Thu Oct 08 23:37:43 2020 -0500
@@ -270,6 +270,7 @@
libpurple/purpleattachment.c
libpurple/purplebuddypresence.c
libpurple/purplechatuser.c
+libpurple/purplecredentialprovider.c
libpurple/purpleimconversation.c
libpurple/purpleprotocolim.c
libpurple/purpleprotocolmedia.c