pidgin/pidgin

Parents 23985cfb6b99
Children e2f15b41266a
Create a new PidginCredentialsPage and associated widgets for the preferences window

Testing Done:
Compiled and ran locally with the secret service provider from https://reviews.imfreedom.org/r/401/

Reviewed at https://reviews.imfreedom.org/r/402/
--- a/doc/reference/pidgin/pidgin-docs.xml Sun Jan 24 05:23:37 2021 -0600
+++ b/doc/reference/pidgin/pidgin-docs.xml Sun Jan 24 05:27:03 2021 -0600
@@ -65,6 +65,8 @@
<xi:include href="xml/pidgincontactlist.xml" />
<xi:include href="xml/pidginconversationwindow.xml" />
<xi:include href="xml/pidgincore.xml" />
+ <xi:include href="xml/pidgincredentialproviderstore.xml" />
+ <xi:include href="xml/pidgincredentialspage.xml" />
<xi:include href="xml/pidgindebug.xml" />
<xi:include href="xml/pidgindialog.xml" />
<xi:include href="xml/pidgingdkpixbuf.xml" />
--- a/libpurple/purplecredentialmanager.c Sun Jan 24 05:23:37 2021 -0600
+++ b/libpurple/purplecredentialmanager.c Sun Jan 24 05:27:03 2021 -0600
@@ -340,6 +340,18 @@
return TRUE;
}
+PurpleCredentialProvider *
+purple_credential_manager_get_active_provider(PurpleCredentialManager *manager)
+{
+ PurpleCredentialManagerPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), NULL);
+
+ priv = purple_credential_manager_get_instance_private(manager);
+
+ return priv->active_provider;
+}
+
void
purple_credential_manager_read_password_async(PurpleCredentialManager *manager,
PurpleAccount *account,
@@ -533,3 +545,23 @@
return purple_credential_provider_write_settings(priv->active_provider,
fields);
}
+
+void
+purple_credential_manager_foreach_provider(PurpleCredentialManager *manager,
+ PurpleCredentialManagerForeachFunc func,
+ gpointer data)
+{
+ GHashTableIter iter;
+ PurpleCredentialManagerPrivate *priv = NULL;
+ gpointer value;
+
+ g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager));
+ g_return_if_fail(func != NULL);
+
+ priv = purple_credential_manager_get_instance_private(manager);
+
+ g_hash_table_iter_init(&iter, priv->providers);
+ while(g_hash_table_iter_next(&iter, NULL, &value)) {
+ func(PURPLE_CREDENTIAL_PROVIDER(value), data);
+ }
+}
\ No newline at end of file
--- a/libpurple/purplecredentialmanager.h Sun Jan 24 05:23:37 2021 -0600
+++ b/libpurple/purplecredentialmanager.h Sun Jan 24 05:27:03 2021 -0600
@@ -57,6 +57,8 @@
* PURPLE_TYPE_CREDENTIAL_MANAGER:
*
* The standard _get_type macro for #PurpleCredentialManager.
+ *
+ * Since: 3.0.0
*/
#define PURPLE_TYPE_CREDENTIAL_MANAGER (purple_credential_manager_get_type())
G_DECLARE_DERIVABLE_TYPE(PurpleCredentialManager, purple_credential_manager,
@@ -95,6 +97,18 @@
};
/**
+ * PurpleCredentialManagerForeachFunc:
+ * @provider: The #PurpleCredentialProvider instance.
+ * @data: User supplied data.
+ *
+ * A function to be used as a callback with
+ * purple_credential_manager_foreach_provider().
+ *
+ * Since: 3.0.0
+ */
+typedef void (*PurpleCredentialManagerForeachFunc)(PurpleCredentialProvider *provider, gpointer data);
+
+/**
* purple_credential_manager_get_default:
*
* Gets the default #PurpleCredentialManager instance.
@@ -146,10 +160,25 @@
* id of @id.
*
* Returns: %TRUE on success or %FALSE with @error set on failure.
+ *
+ * Since: 3.0.0
*/
gboolean purple_credential_manager_set_active_provider(PurpleCredentialManager *manager, const gchar *id, GError **error);
/**
+ * purple_credential_manager_get_active_provider:
+ * @manager: The #PurpleCredentialManager instance.
+ *
+ * Gets the currently active #PurpleCredentialProvider or %NULL if there is no
+ * active provider.
+ *
+ * Returns: (transfer none): The active #PurpleCredentialProvider.
+ *
+ * Since: 3.0.0
+ */
+PurpleCredentialProvider *purple_credential_manager_get_active_provider(PurpleCredentialManager *manager);
+
+/**
* purple_credential_manager_read_password_async:
* @manager: The #PurpleCredentialManager instance.
* @account: The #PurpleAccount whose password to read.
@@ -276,6 +305,19 @@
*/
gboolean purple_credential_manager_write_settings(PurpleCredentialManager *manager, PurpleRequestFields *fields, GError **error);
+
+/**
+ * purple_credential_manager_foreach_provider:
+ * @manager: The #PurpleCredentialManager instance.
+ * @func: (scope call): The #PurpleCredentialManagerForeachFunc to call.
+ * @data: User data to pass to @func.
+ *
+ * Calls @func for each #PurpleCredentialProvider that @manager knows about.
+ *
+ * Since: 3.0.0
+ */
+void purple_credential_manager_foreach_provider(PurpleCredentialManager *manager, PurpleCredentialManagerForeachFunc func, gpointer data);
+
G_END_DECLS
#endif /* PURPLE_CREDENTIAL_MANAGER_H */
--- a/pidgin/glade/pidgin3.xml.in Sun Jan 24 05:23:37 2021 -0600
+++ b/pidgin/glade/pidgin3.xml.in Sun Jan 24 05:27:03 2021 -0600
@@ -8,6 +8,8 @@
<glade-widget-class name="PidginAccountFilterProtocol" generic-name="account_filter_protocol" title="FilterProtocol"/>
<glade-widget-class name="PidginCloseButton" generic-name="close-button" title="CloseButton"/>
<glade-widget-class name="PidginConversationWindow" generic-name="conversation_window" title="ConversationWindow"/>
+ <glade-widget-class name="PidginCredentialProviderStore" generic-name="credential_provider_store" title="CredentialProviderStore"/>
+ <glade-widget-class name="PidginCredentialsPage" generic-name="credentials_page" title="Credentials Page"/>
<glade-widget-class name="PidginDialog" generic-name="dialog" title="Dialog"/>
<glade-widget-class name="PidginInviteDialog" generic-name="invite_dialog" title="InviteDialog"/>
<glade-widget-class name="PidginMenuTray" generic-name="menu_tray" title="MenuTray"/>
@@ -25,6 +27,8 @@
<glade-widget-class-ref name="PidginAccountFilterProtocol"/>
<glade-widget-class-ref name="PidginCloseButton"/>
<glade-widget-class-ref name="PidginConversationWindow"/>
+ <glade-widget-class-ref name="PidginCredentialProviderStore"/>
+ <glade-widget-class-ref name="PidginCredentialsPage"/>
<glade-widget-class-ref name="PidginDialog"/>
<glade-widget-class-ref name="PidginInviteDialog"/>
<glade-widget-class-ref name="PidginMenuTray"/>
--- a/pidgin/meson.build Sun Jan 24 05:23:37 2021 -0600
+++ b/pidgin/meson.build Sun Jan 24 05:27:03 2021 -0600
@@ -44,6 +44,8 @@
'pidgincontactcompletion.c',
'pidginconversationwindow.c',
'pidgincontactlist.c',
+ 'pidgincredentialproviderstore.c',
+ 'pidgincredentialspage.c',
'pidgindebug.c',
'pidgindialog.c',
'pidgingdkpixbuf.c',
@@ -112,6 +114,8 @@
'pidginconversationwindow.h',
'pidgincontactlist.h',
'pidgincore.h',
+ 'pidgincredentialproviderstore.h',
+ 'pidgincredentialspage.h',
'pidgindialog.h',
'pidgindebug.h',
'pidgingdkpixbuf.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgincredentialproviderstore.c Sun Jan 24 05:27:03 2021 -0600
@@ -0,0 +1,178 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "pidgin/pidgincredentialproviderstore.h"
+
+struct _PidginCredentialProviderStore {
+ GtkListStore parent;
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_credential_provider_store_add(PidginCredentialProviderStore *store,
+ PurpleCredentialProvider *provider)
+{
+ GtkTreeIter iter;
+ const gchar *id = NULL, *name = NULL;
+
+ id = purple_credential_provider_get_id(provider);
+ name = purple_credential_provider_get_name(provider);
+
+ gtk_list_store_append(GTK_LIST_STORE(store), &iter);
+ gtk_list_store_set(
+ GTK_LIST_STORE(store),
+ &iter,
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_COLUMN_ID, id,
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_COLUMN_NAME, name,
+ -1
+ );
+}
+
+static void
+pidgin_credential_provider_store_add_helper(PurpleCredentialProvider *provider,
+ gpointer data)
+{
+ pidgin_credential_provider_store_add(PIDGIN_CREDENTIAL_PROVIDER_STORE(data),
+ provider);
+}
+
+static void
+pidgin_credential_provider_store_add_providers(PidginCredentialProviderStore *store)
+{
+ PurpleCredentialManager *manager = NULL;
+
+ manager = purple_credential_manager_get_default();
+
+ purple_credential_manager_foreach_provider(manager,
+ pidgin_credential_provider_store_add_helper,
+ store);
+}
+
+static void
+pidgin_credential_provider_store_remove(PidginCredentialProviderStore *store,
+ PurpleCredentialProvider *provider)
+{
+ GtkTreeIter iter;
+ const gchar *id = NULL;
+
+ id = purple_credential_provider_get_id(provider);
+
+ if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) == FALSE) {
+ purple_debug_warning("credential-provider-store",
+ "asked to remove provider %s but the store is "
+ "empty",
+ id);
+ return;
+ }
+
+ /* walk through the store and look for the provider to remove */
+ do {
+ gchar *found = NULL;
+
+ gtk_tree_model_get(
+ GTK_TREE_MODEL(store),
+ &iter,
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_COLUMN_ID, &found,
+ -1
+ );
+
+ if(purple_strequal(found, id)) {
+ g_free(found);
+
+ gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
+
+ return;
+ }
+
+ g_free(found);
+ } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_credential_provider_store_registered_cb(PurpleCredentialManager *manager,
+ PurpleCredentialProvider *provider,
+ gpointer data)
+{
+ pidgin_credential_provider_store_add(PIDGIN_CREDENTIAL_PROVIDER_STORE(data),
+ provider);
+}
+
+static void
+purple_credential_provider_store_unregistered_cb(PurpleCredentialManager *manager,
+ PurpleCredentialProvider *provider,
+ gpointer data)
+{
+ pidgin_credential_provider_store_remove(PIDGIN_CREDENTIAL_PROVIDER_STORE(data),
+ provider);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginCredentialProviderStore, pidgin_credential_provider_store, GTK_TYPE_LIST_STORE)
+
+static void
+pidgin_credential_provider_store_init(PidginCredentialProviderStore *store) {
+ PurpleCredentialManager *manager = NULL;
+ GType types[PIDGIN_CREDENTIAL_PROVIDER_STORE_N_COLUMNS] = {
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ };
+
+ gtk_list_store_set_column_types(
+ GTK_LIST_STORE(store),
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_N_COLUMNS,
+ types
+ );
+
+ /* add the known providers */
+ pidgin_credential_provider_store_add_providers(store);
+
+ manager = purple_credential_manager_get_default();
+
+ g_signal_connect(G_OBJECT(manager), "provider-registered",
+ G_CALLBACK(purple_credential_provider_store_registered_cb),
+ store);
+ g_signal_connect(G_OBJECT(manager), "provider-unregistered",
+ G_CALLBACK(purple_credential_provider_store_unregistered_cb),
+ store);
+}
+
+static void
+pidgin_credential_provider_store_class_init(PidginCredentialProviderStoreClass *klass) {
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkListStore *
+pidgin_credential_provider_store_new(void) {
+ return GTK_LIST_STORE(g_object_new(PIDGIN_TYPE_CREDENTIAL_PROVIDER_STORE,
+ NULL));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgincredentialproviderstore.h Sun Jan 24 05:27:03 2021 -0600
@@ -0,0 +1,68 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef PIDGIN_CREDENTIAL_PROVIDER_STORE_H
+#define PIDGIN_CREDENTIAL_PROVIDER_STORE_H
+
+/**
+ * SECTION:pidgincredentialproviderstore
+ * @section_id: pidgin-pidgincredentialproviderstore
+ * @short_description: A GtkListStore that keeps track of credential providers.
+ * @title: Credential Provider Store
+ *
+ * #PidginCredentialProviderStore is a #GtkListStore that automatically keeps
+ * track of what credential providers are currently available in libpurple.
+ * It's intended to be used any time that you need to present a credential
+ * provider selection to the user.
+ */
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+typedef enum {
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_COLUMN_ID,
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_COLUMN_NAME,
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_N_COLUMNS,
+} PidginCredentialProviderStoreColumn;
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_CREDENTIAL_PROVIDER_STORE pidgin_credential_provider_store_get_type()
+G_DECLARE_FINAL_TYPE(PidginCredentialProviderStore, pidgin_credential_provider_store, PIDGIN,
+ CREDENTIAL_PROVIDER_STORE, GtkListStore)
+
+/**
+ * pidgin_credential_provider_store_new:
+ *
+ * Creates a new #PidginCredentialProviderStore that can be used anywhere a
+ * #GtkListStore can be used.
+ *
+ * Returns: (transfer full): The new #PidginCredentialProviderStore instance.
+ *
+ * Since: 3.0.0
+ */
+GtkListStore *pidgin_credential_provider_store_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_CREDENTIAL_PROVIDER_STORE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgincredentialspage.c Sun Jan 24 05:27:03 2021 -0600
@@ -0,0 +1,182 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <purple.h>
+
+#include "pidgincredentialspage.h"
+
+#include "pidgincredentialproviderstore.h"
+
+struct _PidginCredentialsPage {
+ GtkBox parent;
+
+ GtkWidget *combo;
+};
+
+G_DEFINE_TYPE(PidginCredentialsPage, pidgin_credentials_page,
+ GTK_TYPE_BOX)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_credentials_page_combo_changed_cb(GtkComboBox *widget, gpointer data) {
+ PidginCredentialsPage *page = PIDGIN_CREDENTIALS_PAGE(data);
+ GtkTreeIter iter;
+
+ if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(page->combo), &iter)) {
+ PurpleCredentialManager *manager = NULL;
+ GError *error = NULL;
+ GtkTreeModel *model = NULL;
+ gchar *id = NULL;
+
+ model = gtk_combo_box_get_model(GTK_COMBO_BOX(page->combo));
+
+ gtk_tree_model_get(model, &iter,
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_COLUMN_ID, &id,
+ -1);
+
+ manager = purple_credential_manager_get_default();
+ if(purple_credential_manager_set_active_provider(manager, id, &error)) {
+ purple_prefs_set_string("/purple/credentials/active-provider", id);
+
+ g_free(id);
+
+ return;
+ }
+
+ purple_debug_warning("credentials-page", "failed to set the active "
+ "credential provider to '%s': %s",
+ id,
+ error ? error->message : "unknown error");
+
+ g_free(id);
+ g_clear_error(&error);
+ }
+}
+
+static void
+pidgin_credentials_page_set_active_provider(PidginCredentialsPage *page,
+ const gchar *new_id)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+
+ model = gtk_combo_box_get_model(GTK_COMBO_BOX(page->combo));
+
+ if(gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ gchar *id = NULL;
+
+ gtk_tree_model_get(model, &iter,
+ PIDGIN_CREDENTIAL_PROVIDER_STORE_COLUMN_ID, &id,
+ -1);
+
+ if(purple_strequal(new_id, id)) {
+ g_signal_handlers_block_by_func(page->combo,
+ pidgin_credentials_page_combo_changed_cb,
+ page);
+
+ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(page->combo),
+ &iter);
+
+ g_signal_handlers_unblock_by_func(page->combo,
+ pidgin_credentials_page_combo_changed_cb,
+ page);
+
+ g_free(id);
+
+ return;
+ }
+
+ g_free(id);
+ } while(gtk_tree_model_iter_next(model, &iter));
+ }
+}
+
+static void
+pidgin_credentials_page_active_provider_changed_cb(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginCredentialsPage *page = PIDGIN_CREDENTIALS_PAGE(data);
+
+ pidgin_credentials_page_set_active_provider(page, (const gchar *)value);
+}
+
+/******************************************************************************
+ * GObjectImplementation
+ *****************************************************************************/
+static void
+pidgin_credentials_page_finalize(GObject *obj) {
+ purple_prefs_disconnect_by_handle(obj);
+
+ G_OBJECT_CLASS(pidgin_credentials_page_parent_class)->finalize(obj);
+}
+
+static void
+pidgin_credentials_page_init(PidginCredentialsPage *page) {
+ const gchar *active = NULL;
+
+ gtk_widget_init_template(GTK_WIDGET(page));
+
+ purple_prefs_add_none("/purple/credentials");
+ purple_prefs_add_string("/purple/credentials/active-provider", NULL);
+
+ purple_prefs_connect_callback(page, "/purple/credentials/active-provider",
+ pidgin_credentials_page_active_provider_changed_cb,
+ page);
+
+ g_signal_connect(G_OBJECT(page->combo), "changed",
+ G_CALLBACK(pidgin_credentials_page_combo_changed_cb),
+ page);
+
+ active = purple_prefs_get_string("/purple/credentials/active-provider");
+ if(active != NULL) {
+ pidgin_credentials_page_set_active_provider(page, active);
+ }
+}
+
+static void
+pidgin_credentials_page_class_init(PidginCredentialsPageClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+ obj_class->finalize = pidgin_credentials_page_finalize;
+
+ gtk_widget_class_set_template_from_resource(
+ widget_class,
+ "/im/pidgin/Pidgin/Prefs/credentials.ui"
+ );
+
+ gtk_widget_class_bind_template_child(widget_class, PidginCredentialsPage,
+ combo);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkWidget *
+pidgin_credentials_page_new(void) {
+ return GTK_WIDGET(g_object_new(PIDGIN_TYPE_CREDENTIALS_PAGE, NULL));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgincredentialspage.h Sun Jan 24 05:27:03 2021 -0600
@@ -0,0 +1,61 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
+# error "only <pidgin.h> may be included directly"
+#endif
+
+#ifndef PIDGIN_CREDENTIALS_PAGE_H
+#define PIDGIN_CREDENTIALS_PAGE_H
+
+/**
+ * SECTION:pidgincredentialspage
+ * @section_id: pidgin-pidgincredentialspage
+ * @short_description: The preferences page for credential management.
+ * @title: Credential management widget
+ *
+ * #PidginCredentialsPage is a widget for the preferences window to let users
+ * choose and configure their credential provider.
+ */
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_CREDENTIALS_PAGE (pidgin_credentials_page_get_type())
+G_DECLARE_FINAL_TYPE(PidginCredentialsPage, pidgin_credentials_page,
+ PIDGIN, CREDENTIALS_PAGE, GtkBox)
+
+/**
+ * pidgin_credentials_page_new:
+ *
+ * Creates a new #PidginCredentialsPage instance.
+ *
+ * Returns: (transfer full): The new #PidginCredentialsPage instance.
+ */
+GtkWidget *pidgin_credentials_page_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_CREDENTIALS_PAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Prefs/credentials.ui Sun Jan 24 05:27:03 2021 -0600
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <requires lib="pidgin" version="3.0"/>
+ <object class="PidginCredentialProviderStore" id="store"/>
+ <template class="PidginCredentialsPage" parent="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Provider:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">store</property>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Credential Provider</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </template>
+</interface>
--- a/pidgin/resources/Prefs/prefs.ui Sun Jan 24 05:23:37 2021 -0600
+++ b/pidgin/resources/Prefs/prefs.ui Sun Jan 24 05:27:03 2021 -0600
@@ -22,6 +22,7 @@
<interface>
<requires lib="Talkatu" version="0.0"/>
<requires lib="gtk+" version="3.20"/>
+ <requires lib="pidgin" version="3.0"/>
<!-- interface-license-type gplv2 -->
<!-- interface-name Pidgin -->
<!-- interface-description Internet Messenger -->
@@ -2639,6 +2640,25 @@
<property name="position">7</property>
</packing>
</child>
+ <child>
+ <object class="PidginCredentialsPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="name">credentials</property>
+ <property name="title" translatable="yes">Credentials</property>
+ <property name="position">8</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">False</property>
@@ -2646,6 +2666,9 @@
<property name="position">1</property>
</packing>
</child>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="expand">True</property>
--- a/pidgin/resources/pidgin.gresource.xml Sun Jan 24 05:23:37 2021 -0600
+++ b/pidgin/resources/pidgin.gresource.xml Sun Jan 24 05:27:03 2021 -0600
@@ -20,6 +20,7 @@
<file compressed="true">Log/log-viewer.ui</file>
<file compressed="true">Plugins/dialog.ui</file>
<file compressed="true">Plugins/menu.ui</file>
+ <file compressed="true">Prefs/credentials.ui</file>
<file compressed="true">Prefs/ip.css</file>
<file compressed="true">Prefs/prefs.ui</file>
<file compressed="true">Prefs/vv.ui</file>
--- a/po/POTFILES.in Sun Jan 24 05:23:37 2021 -0600
+++ b/po/POTFILES.in Sun Jan 24 05:27:03 2021 -0600
@@ -354,6 +354,8 @@
pidgin/pidgincontactcompletion.c
pidgin/pidgincontactlist.c
pidgin/pidginconversationwindow.c
+pidgin/pidgincredentialproviderstore.c
+pidgin/pidgincredentialspage.c
pidgin/pidgindebug.c
pidgin/pidgindialog.c
pidgin/pidgingdkpixbuf.c
@@ -403,6 +405,7 @@
pidgin/resources/Log/log-viewer.ui
pidgin/resources/Plugins/dialog.ui
pidgin/resources/Plugins/menu.ui
+pidgin/resources/Prefs/credentials.ui
pidgin/resources/Prefs/prefs.ui
pidgin/resources/Prefs/vv.ui
pidgin/resources/Privacy/dialog.ui