--- a/ChangeLog.API Tue Jan 26 01:28:00 2021 -0600
+++ b/ChangeLog.API Tue Jan 26 01:46:21 2021 -0600
@@ -49,7 +49,6 @@
* purple_protocols_remove
* purple_protocols_get_all
- * purple_protocols_get_handle
* purple_protocols_uninit
* purple_request_certificate
--- a/doc/reference/libpurple/libpurple-docs.xml Tue Jan 26 01:28:00 2021 -0600
+++ b/doc/reference/libpurple/libpurple-docs.xml Tue Jan 26 01:46:21 2021 -0600
@@ -85,6 +85,7 @@
<xi:include href="xml/purpleprotocolclient.xml" />
<xi:include href="xml/purpleprotocolfactory.xml" />
<xi:include href="xml/purpleprotocolim.xml" />
+ <xi:include href="xml/purpleprotocolmanager.xml" /> <xi:include href="xml/purpleprotocolmedia.xml" />
<xi:include href="xml/purpleprotocolprivacy.xml" />
<xi:include href="xml/purpleprotocolserver.xml" />
--- a/libpurple/core.c Tue Jan 26 01:28:00 2021 -0600
+++ b/libpurple/core.c Tue Jan 26 01:46:21 2021 -0600
@@ -147,7 +147,7 @@
- purple_protocols_init();
+ purple_protocol_manager_startup(); purple_credential_manager_startup(); /* before accounts */
@@ -246,7 +246,7 @@
- purple_protocols_uninit();
+ purple_protocol_manager_shutdown(); --- a/libpurple/meson.build Tue Jan 26 01:28:00 2021 -0600
+++ b/libpurple/meson.build Tue Jan 26 01:46:21 2021 -0600
@@ -63,6 +63,7 @@
'purpleprotocolclient.c',
'purpleprotocolfactory.c',
+ 'purpleprotocolmanager.c', 'purpleprotocolprivacy.c',
'purpleprotocolserver.c',
@@ -154,6 +155,7 @@
'purpleprotocolfactory.h',
+ 'purpleprotocolmanager.h', 'purpleprotocolprivacy.h',
'purpleprotocolserver.h',
--- a/libpurple/protocols.c Tue Jan 26 01:28:00 2021 -0600
+++ b/libpurple/protocols.c Tue Jan 26 01:46:21 2021 -0600
@@ -32,13 +32,12 @@
#include "purpleaccountoption.h"
#include "purplecredentialmanager.h"
#include "purpleprotocolattention.h"
+#include "purpleprotocolmanager.h" #include "purpleprotocolmedia.h"
#include "purpleprotocolserver.h"
-static GHashTable *protocols = NULL;
/**************************************************************************
* GBoxed code for PurpleProtocolChatEntry
**************************************************************************/
@@ -273,6 +272,7 @@
PurpleStatus *old_status, PurpleStatus *new_status)
PurpleProtocol *protocol;
+ PurpleProtocolManager *manager; if (purple_status_is_online(new_status) &&
purple_account_is_disconnected(account) &&
@@ -307,7 +307,9 @@
- protocol = purple_protocols_find(purple_account_get_protocol_id(account));
+ manager = purple_protocol_manager_get_default(); + protocol = purple_protocol_manager_find(manager, + purple_account_get_protocol_id(account)); @@ -371,6 +373,7 @@
PurpleAttentionType *attn;
PurpleProtocol *protocol;
+ PurpleProtocolManager *manager; PurpleIMConversation *im;
@@ -379,7 +382,9 @@
g_return_if_fail(gc != NULL);
g_return_if_fail(who != NULL);
- protocol = purple_protocols_find(purple_account_get_protocol_id(purple_connection_get_account(gc)));
+ manager = purple_protocol_manager_get_default(); + protocol = purple_protocol_manager_find(manager, + purple_account_get_protocol_id(purple_connection_get_account(gc))); g_return_if_fail(PURPLE_IS_PROTOCOL_ATTENTION(protocol));
attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code);
@@ -564,41 +569,19 @@
/**************************************************************************
**************************************************************************/
- * Negative if a before b, 0 if equal, positive if a after b.
-compare_protocol(PurpleProtocol *a, PurpleProtocol *b)
- const gchar *aname = purple_protocol_get_name(a);
- const gchar *bname = purple_protocol_get_name(b);
+#define PURPLE_PROTOCOLS_DOMAIN (g_quark_from_static_string("protocols"))
- return strcmp(aname, bname);
+purple_protocols_find(const char *id) { + PurpleProtocolManager *manager = purple_protocol_manager_get_default(); + return purple_protocol_manager_find(manager, id); -purple_protocols_find(const char *id)
- g_return_val_if_fail(protocols != NULL && id != NULL, NULL);
- return g_hash_table_lookup(protocols, id);
-purple_protocols_add(GType protocol_type, GError **error)
- PurpleProtocol *protocol;
- PurpleProtocolClass *klass;
+purple_protocols_add(GType protocol_type, GError **error) { + PurpleProtocol *protocol = NULL; + PurpleProtocolManager *manager = NULL; if (protocol_type == G_TYPE_INVALID) {
g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
@@ -619,121 +602,29 @@
protocol = g_object_new(protocol_type, NULL);
- g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
- _("Could not create protocol instance"));
- if (!purple_protocol_get_id(protocol)) {
- g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
- _("Protocol does not provide an ID"));
- g_object_unref(protocol);
- if (purple_protocols_find(purple_protocol_get_id(protocol))) {
- g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
- _("A protocol with the ID %s is already added."),
- purple_protocol_get_id(protocol));
- g_object_unref(protocol);
+ manager = purple_protocol_manager_get_default(); + if(!purple_protocol_manager_register(manager, protocol, error)) { - /* Make sure the protocol implements the required functions */
- klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
- if (!klass->login || !klass->close ||
- !klass->status_types || !klass->list_icon )
- g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
- _("Protocol %s does not implement all the functions in "
- "PurpleProtocolClass"), purple_protocol_get_id(protocol));
- g_object_unref(protocol);
- g_hash_table_insert(protocols, g_strdup(purple_protocol_get_id(protocol)),
- purple_debug_info("protocols", "Added protocol %s\n",
- purple_protocol_get_id(protocol));
- purple_signal_emit(purple_protocols_get_handle(), "protocol-added",
-gboolean purple_protocols_remove(PurpleProtocol *protocol, GError **error)
+purple_protocols_remove(PurpleProtocol *protocol, GError **error) { + PurpleProtocolManager *manager; g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), FALSE);
g_return_val_if_fail(purple_protocol_get_id(protocol) != NULL, FALSE);
- if (purple_protocols_find(purple_protocol_get_id(protocol)) == NULL) {
- g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
- _("Protocol %s is not added."),
- purple_protocol_get_id(protocol));
- purple_debug_info("protocols", "Removing protocol %s\n",
- purple_protocol_get_id(protocol));
- purple_signal_emit(purple_protocols_get_handle(), "protocol-removed",
- g_hash_table_remove(protocols, purple_protocol_get_id(protocol));
+ manager = purple_protocol_manager_get_default(); + return purple_protocol_manager_unregister(manager, protocol, error); -purple_protocols_get_all(void)
- PurpleProtocol *protocol;
- g_hash_table_iter_init(&iter, protocols);
- while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&protocol))
- ret = g_list_insert_sorted(ret, protocol, (GCompareFunc)compare_protocol);
-/**************************************************************************
- * Protocols Subsystem API
- **************************************************************************/
-purple_protocols_init(void)
- void *handle = purple_protocols_get_handle();
+purple_protocols_get_all(void) { + PurpleProtocolManager *manager = purple_protocol_manager_get_default(); - protocols = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
- purple_signal_register(handle, "protocol-added",
- purple_marshal_VOID__POINTER,
- G_TYPE_NONE, 1, PURPLE_TYPE_PROTOCOL);
- purple_signal_register(handle, "protocol-removed",
- purple_marshal_VOID__POINTER,
- G_TYPE_NONE, 1, PURPLE_TYPE_PROTOCOL);
+ return purple_protocol_manager_get_all(manager);
-purple_protocols_get_handle(void)
-purple_protocols_uninit(void)
- g_hash_table_destroy(protocols);
--- a/libpurple/protocols.h Tue Jan 26 01:28:00 2021 -0600
+++ b/libpurple/protocols.h Tue Jan 26 01:46:21 2021 -0600
@@ -33,8 +33,6 @@
* @see_also: <link linkend="chapter-signals-protocol">Protocol signals</link>
-#define PURPLE_PROTOCOLS_DOMAIN (g_quark_from_static_string("protocols"))
/**************************************************************************/
/* Basic Protocol Information */
/**************************************************************************/
@@ -419,33 +417,6 @@
GList *purple_protocols_get_all(void);
-/**************************************************************************/
-/* Protocols Subsytem API */
-/**************************************************************************/
- * purple_protocols_init:
- * Initializes the protocols subsystem.
-void purple_protocols_init(void);
- * purple_protocols_get_handle:
- * Returns the protocols subsystem handle.
- * Returns: The protocols subsystem handle.
-void *purple_protocols_get_handle(void);
- * purple_protocols_uninit:
- * Uninitializes the protocols subsystem.
-void purple_protocols_uninit(void);
#endif /* PURPLE_PROTOCOLS_H */
--- a/libpurple/purpleprivate.h Tue Jan 26 01:28:00 2021 -0600
+++ b/libpurple/purpleprivate.h Tue Jan 26 01:46:21 2021 -0600
@@ -220,6 +220,20 @@
void purple_credential_manager_shutdown(void);
+ * purple_protocol_manager_start: + * Starts up the protocol manager by creating the default instance. +void purple_protocol_manager_startup(void); + * purple_protocol_manager_shutdown: + * Shutdowns down the protocol manager by destroying the default instance. +void purple_protocol_manager_shutdown(void); #endif /* PURPLE_PRIVATE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleprotocolmanager.c Tue Jan 26 01:46:21 2021 -0600
@@ -0,0 +1,229 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * 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 "purpleprotocolmanager.h" +#include "purpleprivate.h" +static guint signals[N_SIGNALS] = {0, }; +} PurpleProtocolManagerPrivate; +G_DEFINE_TYPE_WITH_PRIVATE(PurpleProtocolManager, purple_protocol_manager, +static PurpleProtocolManager *default_manager = NULL; +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +purple_protocol_manager_finalize(GObject *obj) { + PurpleProtocolManager *manager = NULL; + PurpleProtocolManagerPrivate *priv = NULL; + manager = PURPLE_PROTOCOL_MANAGER(obj); + priv = purple_protocol_manager_get_instance_private(manager); + g_clear_pointer(&priv->protocols, g_hash_table_destroy); + G_OBJECT_CLASS(purple_protocol_manager_parent_class)->finalize(obj); +purple_protocol_manager_init(PurpleProtocolManager *manager) { + PurpleProtocolManagerPrivate *priv = NULL; + priv = purple_protocol_manager_get_instance_private(manager); + priv->protocols = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, +purple_protocol_manager_class_init(PurpleProtocolManagerClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + obj_class->finalize = purple_protocol_manager_finalize; + * PurpleProtocolManager::protocol-registered: + * @manager: The #PurpleProtocolManager instance. + * @protocol: The #PurpleProtocol that was registered. + * Emitted after @protocol has been registered in @manager. + signals[SIG_REGISTERED] = g_signal_new( + G_OBJECT_CLASS_TYPE(klass), + G_STRUCT_OFFSET(PurpleProtocolManagerClass, registered), + * PurpleProtocolManager::protocol-unregistered: + * @manager: The #PurpleProtocolManager instance. + * @protocol: The #PurpleProtocol that was unregistered. + * Emitted after @protocol has been unregistered from @manager. + signals[SIG_UNREGISTERED] = g_signal_new( + G_OBJECT_CLASS_TYPE(klass), + G_STRUCT_OFFSET(PurpleProtocolManagerClass, unregistered), +/****************************************************************************** + *****************************************************************************/ +purple_protocol_manager_startup(void) { + if(default_manager == NULL) { + default_manager = g_object_new(PURPLE_TYPE_PROTOCOL_MANAGER, NULL); +purple_protocol_manager_shutdown(void) { + g_clear_object(&default_manager); +/****************************************************************************** + *****************************************************************************/ +purple_protocol_manager_get_default(void) { + return default_manager; +purple_protocol_manager_register(PurpleProtocolManager *manager, + PurpleProtocol *protocol, GError **error) + PurpleProtocolManagerPrivate *priv = NULL; + const gchar *id = NULL; + g_return_val_if_fail(PURPLE_IS_PROTOCOL_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), FALSE); + priv = purple_protocol_manager_get_instance_private(manager); + id = purple_protocol_get_id(protocol); + if(g_hash_table_lookup(priv->protocols, id) != NULL) { + g_set_error(error, PURPLE_PROTOCOL_MANAGER_DOMAIN, 0, + _("protocol %s is already registered"), id); + g_hash_table_insert(priv->protocols, g_strdup(id), g_object_ref(protocol)); + g_signal_emit(G_OBJECT(manager), signals[SIG_REGISTERED], 0, protocol); +purple_protocol_manager_unregister(PurpleProtocolManager *manager, + PurpleProtocol *protocol, GError **error) + PurpleProtocolManagerPrivate *priv = NULL; + const gchar *id = NULL; + g_return_val_if_fail(PURPLE_IS_PROTOCOL_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), FALSE); + /* We need to hold a reference on the protocol as typically we will be + * holding the only reference on the protocol when this is called and we + * will need to pass it to the signal emission after it's removed from the + * hash table that'll unref it. + g_object_ref(G_OBJECT(protocol)); + priv = purple_protocol_manager_get_instance_private(manager); + id = purple_protocol_get_id(protocol); + if(g_hash_table_remove(priv->protocols, id)) { + g_signal_emit(G_OBJECT(manager), signals[SIG_UNREGISTERED], 0, + g_set_error(error, PURPLE_PROTOCOL_MANAGER_DOMAIN, 0, + _("protocol %s is not registered"), id); + g_object_unref(G_OBJECT(protocol)); +purple_protocol_manager_find(PurpleProtocolManager *manager, const gchar *id) { + PurpleProtocolManagerPrivate *priv = NULL; + g_return_val_if_fail(PURPLE_PROTOCOL_MANAGER(manager), NULL); + priv = purple_protocol_manager_get_instance_private(manager); + value = g_hash_table_lookup(priv->protocols, id); + return PURPLE_PROTOCOL(value); +purple_protocol_manager_get_all(PurpleProtocolManager *manager) { + PurpleProtocolManagerPrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_PROTOCOL_MANAGER(manager), NULL); + priv = purple_protocol_manager_get_instance_private(manager); + return g_hash_table_get_values(priv->protocols); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleprotocolmanager.h Tue Jan 26 01:46:21 2021 -0600
@@ -0,0 +1,160 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * 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(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION) +# error "only <pidgin.h> may be included directly" +#ifndef PURPLE_PROTOCOL_MANAGER_H +#define PURPLE_PROTOCOL_MANAGER_H +#include <glib-object.h> + * SECTION:purpleprotocolmanager + * @section_id: libpurple-purpleprotocolmanager + * @title: Purple Protocol Manager + * #PurpleProtocolManager keeps track of all protocols and emits signals when + * protocols are registered and unregistered. + * PURPLE_PROTOCOL_MANAGER_DOMAIN: + * A #GError domain for errors from #PurpleProtocolManager. +#define PURPLE_PROTOCOL_MANAGER_DOMAIN (g_quark_from_static_string("purple-protocol-manager")) + * PURPLE_TYPE_PROTOCOL_MANAGER: + * The standard _get_type macro for #PurpleProtocolManager. +#define PURPLE_TYPE_PROTOCOL_MANAGER (purple_protocol_manager_get_type()) +G_DECLARE_DERIVABLE_TYPE(PurpleProtocolManager, purple_protocol_manager, + PURPLE, PROTOCOL_MANAGER, GObject) + * PurpleProtocolManager: + * An opaque data structure that manages protocols. + * PurpleProtocolManagerClass: + * @registered: The default signal handler for when a protocol is registered. + * @unregistered: The default signal handler for when a protocol is + * The class structure for #PurpleProtocolManager. +struct _PurpleProtocolManagerClass { + void (*registered)(PurpleProtocolManager *manager, PurpleProtocol *protocol); + void (*unregistered)(PurpleProtocolManager *manager, PurpleProtocol *protocol); + * purple_protocol_manager_get_default: + * Gets the default #PurpleProtocolManager instance. + * Returns: (transfer none): The default #PurpleProtocolManager instance. +PurpleProtocolManager *purple_protocol_manager_get_default(void); + * purple_protocol_manager_register: + * @manager: The #PurpleProtocolManager instance. + * @protocol: The #PurpleProtocol to register. + * @error: (out) (optional) (nullable): A return address for a #GError. + * Registers @protocol with @manager. + * Returns: %TRUE if @protocol was successfully registered with @manager, +gboolean purple_protocol_manager_register(PurpleProtocolManager *manager, PurpleProtocol *protocol, GError **error); + * purple_protocol_manager_unregister: + * @manager: The #PurpleProtocolManager instance. + * @protocol: The #PurpleProtocol to unregister. + * @error: (out) (optional) (nullable): A return address for a #GError. + * Unregisters @protocol from @manager. + * Returns: %TRUE if @protocol was successfully unregistered from @manager, +gboolean purple_protocol_manager_unregister(PurpleProtocolManager *manager, PurpleProtocol *protocol, GError **error); + * purple_protocol_manager_find: + * @manager: The #PurpleProtocolManager instance. + * @id: The id of the #PurpleProtocol to find. + * Gets the #PurpleProtocol identified by @id if found, otherwise %NULL. + * Returns: (transfer none): The #PurpleProtocol identified by @id or %NULL. +PurpleProtocol *purple_protocol_manager_find(PurpleProtocolManager *manager, const gchar *id); + * purple_protocol_manager_get_all: + * @manager: The #PurpleProtocolManager instance. + * Gets a sorted list of all #PurpleProtocols that are currently registered in + * Returns: (transfer container) (element-type PurpleProtocol): The list + * containing all of the #PurpleProtocols registered with @manager. +GList *purple_protocol_manager_get_all(PurpleProtocolManager *manager); +#endif /* PURPLE_PROTOCOL_MANAGER_H */ --- a/pidgin/pidginprotocolstore.c Tue Jan 26 01:28:00 2021 -0600
+++ b/pidgin/pidginprotocolstore.c Tue Jan 26 01:46:21 2021 -0600
@@ -65,8 +65,13 @@
pidgin_protocol_store_add_protocols(PidginProtocolStore *store) {
- g_list_foreach(purple_protocols_get_all(),
- pidgin_protocol_store_add_protocol_helper, store);
+ PurpleProtocolManager *manager = purple_protocol_manager_get_default(); + GList *protocols = NULL; + protocols = purple_protocol_manager_get_all(manager); + g_list_foreach(protocols, pidgin_protocol_store_add_protocol_helper, + g_list_free(protocols); @@ -107,17 +112,18 @@
*****************************************************************************/
-pidgin_protocol_store_protocol_added_cb(PurpleProtocol *protocol,
+pidgin_protocol_store_registered_cb(PurpleProtocolManager *manager, + PurpleProtocol *protocol, gpointer data) pidgin_protocol_store_add_protocol(PIDGIN_PROTOCOL_STORE(data), protocol);
-pidgin_protocol_store_protocol_removed_cb(PurpleProtocol *protocol,
+pidgin_protocol_store_unregistered_cb(PurpleProtocolManager *manager, + PurpleProtocol *protocol, gpointer data) - pidgin_protocol_store_remove_protocol(PIDGIN_PROTOCOL_STORE(data), protocol);
+ pidgin_protocol_store_remove_protocol(PIDGIN_PROTOCOL_STORE(data), /******************************************************************************
@@ -127,7 +133,7 @@
pidgin_protocol_store_init(PidginProtocolStore *store) {
- gpointer protocols_handle = NULL;
+ PurpleProtocolManager *manager = NULL; GType types[PIDGIN_PROTOCOL_STORE_N_COLUMNS] = {
@@ -144,13 +150,11 @@
pidgin_protocol_store_add_protocols(store);
/* add the signal handlers to dynamically add/remove protocols */
- protocols_handle = purple_protocols_get_handle();
- purple_signal_connect(protocols_handle, "protocol-added", store,
- G_CALLBACK(pidgin_protocol_store_protocol_added_cb),
- purple_signal_connect(protocols_handle, "protocol-removed", store,
- G_CALLBACK(pidgin_protocol_store_protocol_removed_cb),
+ manager = purple_protocol_manager_get_default(); + g_signal_connect(G_OBJECT(manager), "registered", + G_CALLBACK(pidgin_protocol_store_registered_cb), store); + g_signal_connect(G_OBJECT(manager), "unregistered", + G_CALLBACK(pidgin_protocol_store_unregistered_cb), store); --- a/po/POTFILES.in Tue Jan 26 01:28:00 2021 -0600
+++ b/po/POTFILES.in Tue Jan 26 01:46:21 2021 -0600
@@ -261,6 +261,7 @@
libpurple/purpleprotocolfactory.c
libpurple/purpleprotocolim.c
libpurple/purpleprotocolmedia.c
+libpurple/purpleprotocolmanager.c libpurple/purpleprotocolprivacy.c
libpurple/purpleprotocolserver.c