Split PurpleProtocolChat to its own file and clean up the API.

Mon, 11 Jan 2021 01:51:14 -0600

author
Gary Kramlich <grim@reaperworld.com>
date
Mon, 11 Jan 2021 01:51:14 -0600
changeset 40697
81f81f5d2f39
parent 40696
cf58ec89b1e4
child 40698
d06e6660b39c

Split PurpleProtocolChat to its own file and clean up the API.

Testing Done:
* Compiled and tested bonjour (not affected) and irc locally.
* Built and verified the docs.
* verified `ninja pidgin-pot` was successful.

Bugs closed: 17457

Reviewed at https://reviews.imfreedom.org/r/406/

doc/reference/libpurple/libpurple-docs.xml file | annotate | diff | comparison | revisions
finch/gntblist.c file | annotate | diff | comparison | revisions
finch/gntconv.c file | annotate | diff | comparison | revisions
libpurple/buddylist.c file | annotate | diff | comparison | revisions
libpurple/chat.c file | annotate | diff | comparison | revisions
libpurple/meson.build file | annotate | diff | comparison | revisions
libpurple/protocol.c file | annotate | diff | comparison | revisions
libpurple/protocol.h file | annotate | diff | comparison | revisions
libpurple/protocols/facebook/facebook.c file | annotate | diff | comparison | revisions
libpurple/protocols/gg/chat.c file | annotate | diff | comparison | revisions
libpurple/protocols/gg/chat.h file | annotate | diff | comparison | revisions
libpurple/protocols/irc/irc.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/chat.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/chat.h file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/jabber.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/message.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/message.h file | annotate | diff | comparison | revisions
libpurple/protocols/novell/novell.c file | annotate | diff | comparison | revisions
libpurple/protocols/null/nullprpl.c file | annotate | diff | comparison | revisions
libpurple/protocols/sametime/sametime.c file | annotate | diff | comparison | revisions
libpurple/protocols/zephyr/zephyr.c file | annotate | diff | comparison | revisions
libpurple/purpleprotocolchat.c file | annotate | diff | comparison | revisions
libpurple/purpleprotocolchat.h file | annotate | diff | comparison | revisions
libpurple/server.c file | annotate | diff | comparison | revisions
pidgin/gtkblist.c file | annotate | diff | comparison | revisions
pidgin/gtkconv.c file | annotate | diff | comparison | revisions
pidgin/gtkutils.c file | annotate | diff | comparison | revisions
po/POTFILES.in file | annotate | diff | comparison | revisions
--- a/doc/reference/libpurple/libpurple-docs.xml	Mon Jan 11 01:08:47 2021 -0600
+++ b/doc/reference/libpurple/libpurple-docs.xml	Mon Jan 11 01:51:14 2021 -0600
@@ -74,6 +74,7 @@
       <xi:include href="xml/purplemarkup.xml" />
       <xi:include href="xml/purplepresence.xml" />
       <xi:include href="xml/purpleprotocolattention.xml" />
+      <xi:include href="xml/purpleprotocolchat.xml" />
       <xi:include href="xml/purpleprotocolclient.xml" />
       <xi:include href="xml/purpleprotocolfactory.xml" />
       <xi:include href="xml/purpleprotocolim.xml" />
--- a/finch/gntblist.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/finch/gntblist.c	Mon Jan 11 01:51:14 2021 -0600
@@ -644,7 +644,7 @@
 
 	gc = purple_account_get_connection(account);
 	protocol = purple_connection_get_protocol(gc);
-	hash = purple_protocol_chat_iface_info_defaults(protocol, gc, name);
+	hash = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc, name);
 
 	chat = purple_chat_new(account, name, hash);
 
@@ -985,13 +985,15 @@
 	PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
 	PurpleRequestField *field;
 	GList *parts, *iter;
+	PurpleProtocol *protocol;
 	PurpleProtocolChatEntry *pce;
 	PurpleConnection *gc;
 
 	purple_request_fields_add_group(fields, group);
 
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	parts = purple_protocol_chat_iface_info(purple_connection_get_protocol(gc), gc);
+	protocol = purple_connection_get_protocol(gc);
+	parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc);
 
 	for (iter = parts; iter; iter = iter->next) {
 		pce = iter->data;
@@ -1275,7 +1277,7 @@
 		account = purple_chat_get_account(c);
 		protocol = purple_protocols_find(purple_account_get_protocol_id(account));
 		if (protocol) {
-			name = purple_protocol_chat_iface_get_name(protocol, purple_chat_get_components(c));
+			name = purple_protocol_chat_get_name(PURPLE_PROTOCOL_CHAT(protocol), purple_chat_get_components(c));
 		}
 	} else if (PURPLE_IS_CONTACT(node)) {
 		finch_log_show_contact((PurpleContact *)node);
@@ -2669,7 +2671,7 @@
 	chat = purple_blist_find_chat(account, name);
 	if (chat == NULL) {
 		PurpleProtocol *protocol = purple_connection_get_protocol(gc);
-		hash = purple_protocol_chat_iface_info_defaults(protocol, gc, name);
+		hash = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc, name);
 	} else {
 		hash = purple_chat_get_components(chat);
 	}
--- a/finch/gntconv.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/finch/gntconv.c	Mon Jan 11 01:51:14 2021 -0600
@@ -306,7 +306,7 @@
 			chat = find_chat_for_conversation(conv);
 			if (chat == NULL) {
 				PurpleProtocol *protocol = purple_connection_get_protocol(gc);
-				comps = purple_protocol_chat_iface_info_defaults(protocol, gc,
+				comps = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc,
 						purple_conversation_get_name(conv));
 			} else {
 				comps = purple_chat_get_components(chat);
@@ -658,7 +658,7 @@
 
 	protocol = purple_connection_get_protocol(gc);
 	if (protocol)
-		realname = purple_protocol_chat_iface_get_user_real_name(protocol, gc,
+		realname = purple_protocol_chat_get_user_real_name(PURPLE_PROTOCOL_CHAT(protocol), gc,
 				purple_chat_conversation_get_id(
 				PURPLE_CHAT_CONVERSATION(fc->active_conv)), name);
 
--- a/libpurple/buddylist.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/buddylist.c	Mon Jan 11 01:51:14 2021 -0600
@@ -31,6 +31,7 @@
 #include "prefs.h"
 #include "protocol.h"
 #include "purpleprivate.h"
+#include "purpleprotocolchat.h"
 #include "purpleprotocolclient.h"
 #include "server.h"
 #include "signals.h"
@@ -1779,7 +1780,7 @@
 				if (account != purple_chat_get_account(chat))
 					continue;
 
-				parts = purple_protocol_chat_iface_info(protocol, 
+				parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol),
 					purple_account_get_connection(purple_chat_get_account(chat)));
 
 				pce = parts->data;
--- a/libpurple/chat.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/chat.c	Mon Jan 11 01:51:14 2021 -0600
@@ -22,6 +22,7 @@
  */
 #include "internal.h"
 #include "chat.h"
+#include "purpleprotocolchat.h"
 #include "util.h"
 
 typedef struct _PurpleChatPrivate       PurpleChatPrivate;
@@ -86,7 +87,8 @@
 
 	if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info)) {
 		PurpleProtocolChatEntry *pce;
-		GList *parts = purple_protocol_chat_iface_info(protocol, purple_account_get_connection(priv->account));
+		GList *parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol),
+		                                         purple_account_get_connection(priv->account));
 		pce = parts->data;
 		ret = g_hash_table_lookup(priv->components, pce->identifier);
 		g_list_free_full(parts, g_free);
--- a/libpurple/meson.build	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/meson.build	Mon Jan 11 01:51:14 2021 -0600
@@ -59,6 +59,7 @@
 	'purplemessage.c',
 	'purplepresence.c',
 	'purpleprotocolattention.c',
+	'purpleprotocolchat.c',
 	'purpleprotocolclient.c',
 	'purpleprotocolfactory.c',
 	'purpleprotocolim.c',
@@ -147,6 +148,7 @@
 	'purplemessage.h',
 	'purplepresence.h',
 	'purpleprotocolattention.h',
+	'purpleprotocolchat.h',
 	'purpleprotocolclient.h',
 	'purpleprotocolfactory.h',
 	'purpleprotocolim.h',
--- a/libpurple/protocol.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocol.c	Mon Jan 11 01:51:14 2021 -0600
@@ -402,110 +402,3 @@
 
 #undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
 #undef DEFINE_PROTOCOL_FUNC
-
-/**************************************************************************
- * Protocol Chat Interface API
- **************************************************************************/
-#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
-	PurpleProtocolChatInterface *chat_iface = \
-		PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol); \
-	if (chat_iface && chat_iface->funcname) \
-		chat_iface->funcname(__VA_ARGS__);
-
-#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
-	PurpleProtocolChatInterface *chat_iface = \
-		PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol); \
-	if (chat_iface && chat_iface->funcname) \
-		return chat_iface->funcname(__VA_ARGS__); \
-	else \
-		return defaultreturn;
-
-GType
-purple_protocol_chat_iface_get_type(void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY(type == 0)) {
-		static const GTypeInfo info = {
-			.class_size = sizeof(PurpleProtocolChatInterface),
-		};
-
-		type = g_type_register_static(G_TYPE_INTERFACE,
-				"PurpleProtocolChatInterface", &info, 0);
-	}
-	return type;
-}
-
-GList *
-purple_protocol_chat_iface_info(PurpleProtocol *protocol, PurpleConnection *gc)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, info, gc);
-}
-
-GHashTable *
-purple_protocol_chat_iface_info_defaults(PurpleProtocol *protocol,
-		PurpleConnection *gc, const char *chat_name)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, info_defaults, gc,
-			chat_name);
-}
-
-void
-purple_protocol_chat_iface_join(PurpleProtocol *protocol, PurpleConnection *gc,
-		GHashTable *components)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, join, gc, components);
-}
-
-void
-purple_protocol_chat_iface_reject(PurpleProtocol *protocol,
-		PurpleConnection *gc, GHashTable *components)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, reject, gc, components);
-}
-
-char *
-purple_protocol_chat_iface_get_name(PurpleProtocol *protocol,
-		GHashTable *components)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_name, components);
-}
-
-void
-purple_protocol_chat_iface_invite(PurpleProtocol *protocol,
-		PurpleConnection *gc, int id, const char *message, const char *who)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, invite, gc, id, message, who);
-}
-
-void
-purple_protocol_chat_iface_leave(PurpleProtocol *protocol, PurpleConnection *gc,
-		int id)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, leave, gc, id);
-}
-
-int 
-purple_protocol_chat_iface_send(PurpleProtocol *protocol, PurpleConnection *gc,
-		int id, PurpleMessage *msg)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, send, gc, id, msg);
-}
-
-char *
-purple_protocol_chat_iface_get_user_real_name(PurpleProtocol *protocol,
-		PurpleConnection *gc, int id, const char *who)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_user_real_name, gc, id,
-			who);
-}
-
-void
-purple_protocol_chat_iface_set_topic(PurpleProtocol *protocol,
-		PurpleConnection *gc, int id, const char *topic)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, set_topic, gc, id, topic);
-}
-
-#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
-#undef DEFINE_PROTOCOL_FUNC
--- a/libpurple/protocol.h	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocol.h	Mon Jan 11 01:51:14 2021 -0600
@@ -276,105 +276,6 @@
 #define PURPLE_PROTOCOL_SERVER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_SERVER, \
                                                PurpleProtocolServerInterface))
 
-#define PURPLE_TYPE_PROTOCOL_CHAT (purple_protocol_chat_iface_get_type())
-
-typedef struct _PurpleProtocolChatInterface PurpleProtocolChatInterface;
-
-/**
- * PurpleProtocolChatInterface:
- * @info: Returns a list of #PurpleProtocolChatEntry structs, which represent
- *        information required by the protocol to join a chat. libpurple will
- *        call join_chat along with the information filled by the user.
- *        <sbr/>Returns: A list of #PurpleProtocolChatEntry's
- * @info_defaults: Returns a hashtable which maps #PurpleProtocolChatEntry
- *                 struct identifiers to default options as strings based on
- *                 @chat_name. The resulting hashtable should be created with
- *                 #g_hash_table_new_full(#g_str_hash, #g_str_equal, %NULL,
- *                 #g_free). Use @get_name if you instead need to extract a chat
- *                 name from a hashtable.
- *                 <sbr/>@chat_name: The chat name to be turned into components
- *                 <sbr/>Returns: Hashtable containing the information extracted
- *                                from @chat_name
- * @join: Called when the user requests joining a chat. Should arrange for
- *        purple_serv_got_joined_chat() to be called.
- *        <sbr/>@components: A hashtable containing information required to join
- *                           the chat as described by the entries returned by
- *                           @info. It may also be called when accepting an
- *                           invitation, in which case this matches the data
- *                           parameter passed to purple_serv_got_chat_invite().
- * @reject: Called when the user refuses a chat invitation.
- *          <sbr/>@components: A hashtable containing information required to
- *                            join the chat as passed to purple_serv_got_chat_invite().
- * @get_name: Returns a chat name based on the information in components. Use
- *            @info_defaults if you instead need to generate a hashtable from a
- *            chat name.
- *            <sbr/>@components: A hashtable containing information about the
- *                               chat.
- * @invite: Invite a user to join a chat.
- *          <sbr/>@id:      The id of the chat to invite the user to.
- *          <sbr/>@message: A message displayed to the user when the invitation
- *                          is received.
- *          <sbr/>@who:     The name of the user to send the invation to.
- * @leave: Called when the user requests leaving a chat.
- *         <sbr/>@id: The id of the chat to leave
- * @send: Send a message to a chat.
- *              <sbr/>This protocol function should return a positive value on
- *              success. If the message is too big to be sent, return
- *              <literal>-E2BIG</literal>. If the account is not connected,
- *              return <literal>-ENOTCONN</literal>. If the protocol is unable
- *              to send the message for another reason, return some other
- *              negative value. You can use one of the valid #errno values, or
- *              just big something.
- *              <sbr/>@id:      The id of the chat to send the message to.
- *              <sbr/>@msg:     The message to send to the chat.
- *              <sbr/>Returns:  A positive number or 0 in case of success, a
- *                              negative error number in case of failure.
- * @get_user_real_name: Gets the real name of a participant in a chat. For
- *                      example, on XMPP this turns a chat room nick
- *                      <literal>foo</literal> into
- *                      <literal>room\@server/foo</literal>.
- *                      <sbr/>@gc:  the connection on which the room is.
- *                      <sbr/>@id:  the ID of the chat room.
- *                      <sbr/>@who: the nickname of the chat participant.
- *                      <sbr/>Returns: the real name of the participant. This
- *                                     string must be freed by the caller.
- *
- * The protocol chat interface.
- *
- * This interface provides callbacks needed by protocols that implement chats.
- */
-struct _PurpleProtocolChatInterface
-{
-	/*< private >*/
-	GTypeInterface parent_iface;
-
-	/*< public >*/
-	GList *(*info)(PurpleConnection *connection);
-
-	GHashTable *(*info_defaults)(PurpleConnection *connection, const char *chat_name);
-
-	void (*join)(PurpleConnection *connection, GHashTable *components);
-
-	void (*reject)(PurpleConnection *connection, GHashTable *components);
-
-	char *(*get_name)(GHashTable *components);
-
-	void (*invite)(PurpleConnection *connection, int id,
-					const char *message, const char *who);
-
-	void (*leave)(PurpleConnection *connection, int id);
-
-	int  (*send)(PurpleConnection *connection, int id, PurpleMessage *msg);
-
-	char *(*get_user_real_name)(PurpleConnection *gc, int id, const char *who);
-
-	void (*set_topic)(PurpleConnection *gc, int id, const char *topic);
-};
-
-#define PURPLE_IS_PROTOCOL_CHAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_CHAT))
-#define PURPLE_PROTOCOL_CHAT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CHAT, \
-                                             PurpleProtocolChatInterface))
-
 /**
  * PURPLE_PROTOCOL_IMPLEMENTS:
  * @protocol: The protocol in which to check
@@ -585,166 +486,6 @@
 /* Protocol Chat Interface API                                            */
 /**************************************************************************/
 
-/**
- * purple_protocol_chat_iface_get_type:
- *
- * Returns: The #GType for the protocol chat interface.
- *
- * Since: 3.0.0
- */
-GType purple_protocol_chat_iface_get_type(void);
-
-/**
- * purple_protocol_chat_iface_info:
- * @protocol: The #PurpleProtocol instance.
- * @connection: The #PurpleConnection instance.
- *
- * Gets the list of #PurpleProtocolChatEntry's that are required to join a
- * multi user chat.
- *
- * Returns: (transfer full) (element-type PurpleProtocolChatEntry): The list
- *          of #PurpleProtocolChatEntry's that are used to join a chat.
- *
- * Since: 3.0.0
- */
-GList *purple_protocol_chat_iface_info(PurpleProtocol *protocol,
-		PurpleConnection *connection);
-
-/**
- * purple_protocol_chat_iface_info_defaults:
- * @protocol: The #PurpleProtocol instance
- * @connection: The #PurpleConnection instance
- * @chat_name: The name of the chat
- *
- * Returns a #GHashTable of the default protocol dependent components that will
- * be passed to #purple_protocol_chat_iface_join.
- *
- * Returns: (transfer full) (element-type utf8 utf8): The values that will be
- *          used to join the chat.
- *
- * Since: 3.0.0
- */
-GHashTable *purple_protocol_chat_iface_info_defaults(PurpleProtocol *protocol,
-		PurpleConnection *connection, const char *chat_name);
-
-/**
- * purple_protocol_chat_iface_join:
- * @protocol: The #PurpleProtocol instance
- * @connection: The #PurpleConnection instance
- * @components: (element-type utf8 utf8): The protocol dependent join
- *              components
- *
- * Joins the chat described in @components.
- *
- * Since: 3.0.0
- */
-void purple_protocol_chat_iface_join(PurpleProtocol *protocol, PurpleConnection *connection,
-		GHashTable *components);
-
-/**
- * purple_protocol_chat_iface_reject:
- * @protocol: The #PurpleProtocol instance
- * @connection: The #PurpleConnection instance
- * @components: (element-type utf8 utf8): The protocol dependent join
- *              components
- *
- * Not quite sure exactly what this does or where it's used.  Please fill in
- * the details if you know.
- *
- * Since: 3.0.0
- */
-void purple_protocol_chat_iface_reject(PurpleProtocol *protocol,
-		PurpleConnection *connection, GHashTable *components);
-
-/**
- * purple_protocol_chat_iface_get_name:
- * @protocol: The #PurpleProtocol instance
- * @components: (element-type utf8 utf8): The protocol dependent join
- *              components
- *
- * Gets the name from @components.
- *
- * Returns: (transfer full): The chat name from @components.
- *
- * Since: 3.0.0
- */
-char *purple_protocol_chat_iface_get_name(PurpleProtocol *protocol,
-		GHashTable *components);
-
-/**
- * purple_protocol_chat_iface_invite:
- * @protocol: The #PurpleProtocol instance
- * @connection: The #PurpleConnection instance
- * @id: The id of the chat
- * @message: The invite message
- * @who: The target of the invite
- *
- * Sends an invite to @who with @message.
- *
- * Since: 3.0.0
- */
-void purple_protocol_chat_iface_invite(PurpleProtocol *protocol,
-		PurpleConnection *connection, int id, const char *message, const char *who);
-
-/**
- * purple_protocol_chat_iface_leave:
- * @protocol: The #PurpleProtocol instance
- * @connection: The #PurpleConnection instance
- * @id: The id of the chat
- *
- * Leaves the chat identified by @id.
- *
- * Since: 3.0.0
- */
-void purple_protocol_chat_iface_leave(PurpleProtocol *protocol, PurpleConnection *connection,
-		int id);
-
-/**
- * purple_protocol_chat_iface_send:
- * @protocol: The #PurpleProtocol instance
- * @connection: The #PurpleConnection instance
- * @id: The id of the chat
- * @msg: The message to send
- *
- * Sends @msg to the chat identified by @id.
- *
- * Returns: 0 on success, non-zero on failure.
- *
- * Since: 3.0.0
- */
-int  purple_protocol_chat_iface_send(PurpleProtocol *protocol, PurpleConnection *connection,
-		int id, PurpleMessage *msg);
-
-/**
- * purple_protocol_chat_iface_get_user_real_name:
- * @protocol: The #PurpleProtocol instance
- * @gc: The #PurpleConnection instance
- * @id: The id of the chat
- * @who: The username
- *
- * Gets the real name of @who.
- *
- * Returns: (transfer full): The realname of @who.
- *
- * Since: 3.0.0
- */
-char *purple_protocol_chat_iface_get_user_real_name(PurpleProtocol *protocol,
-		PurpleConnection *gc, int id, const char *who);
-
-/**
- * purple_protocol_chat_iface_set_topic:
- * @protocol: The #PurpleProtocol instance
- * @gc: The #PurpleConnection instance
- * @id: The id of the chat
- * @topic: The new topic
- *
- * Sets the topic for the chat with id @id to @topic.
- *
- * Since: 3.0.0
- */
-void purple_protocol_chat_iface_set_topic(PurpleProtocol *protocol,
-		PurpleConnection *gc, int id, const char *topic);
-
 G_END_DECLS
 
 #endif /* PURPLE_PROTOCOL_H */
--- a/libpurple/protocols/facebook/facebook.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/facebook/facebook.c	Mon Jan 11 01:51:14 2021 -0600
@@ -1231,7 +1231,7 @@
 }
 
 static GList *
-fb_chat_info()
+fb_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *connection)
 {
 	GList *pces = NULL;
 	PurpleProtocolChatEntry *pce;
@@ -1246,7 +1246,8 @@
 }
 
 static GHashTable *
-fb_chat_info_defaults(PurpleConnection *gc, const gchar *name)
+fb_chat_info_defaults(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                      const gchar *name)
 {
 	GHashTable *data;
 
@@ -1257,7 +1258,8 @@
 }
 
 static void
-fb_chat_join(PurpleConnection *gc, GHashTable *data)
+fb_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+             GHashTable *data)
 {
 	const gchar *name;
 	FbApi *api;
@@ -1295,7 +1297,7 @@
 }
 
 static gchar *
-fb_chat_get_name(GHashTable *data)
+fb_chat_get_name(PurpleProtocolChat *protocol_chat, GHashTable *data)
 {
 	const gchar *name;
 
@@ -1306,8 +1308,8 @@
 }
 
 static void
-fb_chat_invite(PurpleConnection *gc, gint id, const gchar *msg,
-               const gchar *who)
+fb_chat_invite(PurpleProtocolChat *protocol_chat,  PurpleConnection *gc,
+               gint id, const gchar *msg, const gchar *who)
 {
 	const gchar *name;
 	FbApi *api;
@@ -1339,7 +1341,8 @@
 }
 
 static gint
-fb_chat_send(PurpleConnection *gc, gint id, PurpleMessage *msg)
+fb_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, gint id,
+             PurpleMessage *msg)
 {
 	const gchar *name;
 	const gchar *text;
@@ -1372,7 +1375,8 @@
 }
 
 static void
-fb_chat_set_topic(PurpleConnection *gc, gint id, const gchar *topic)
+fb_chat_set_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                  gint id, const gchar *topic)
 {
 	const gchar *name;
 	FbApi *api;
--- a/libpurple/protocols/gg/chat.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/gg/chat.c	Mon Jan 11 01:51:14 2021 -0600
@@ -293,8 +293,8 @@
 	purple_chat_conversation_remove_user(chat->conv, ggp_uin_to_str(uin), NULL);
 }
 
-GList * ggp_chat_info(PurpleConnection *gc)
-{
+GList *
+ggp_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *gc) {
 	GList *m = NULL;
 	PurpleProtocolChatEntry *pce;
 
@@ -307,7 +307,9 @@
 	return m;
 }
 
-GHashTable * ggp_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
+GHashTable *
+ggp_chat_info_defaults(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                       const gchar *chat_name)
 {
 	GHashTable *defaults;
 
@@ -319,8 +321,8 @@
 	return defaults;
 }
 
-char * ggp_chat_get_name(GHashTable *components)
-{
+gchar *
+ggp_chat_get_name(PurpleProtocolChat *protocol_chat, GHashTable *components) {
 	return g_strdup((gchar*)g_hash_table_lookup(components, "id"));
 }
 
@@ -347,7 +349,9 @@
 	return id;
 }
 
-void ggp_chat_join(PurpleConnection *gc, GHashTable *components)
+void
+ggp_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+              GHashTable *components)
 {
 	ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
 	GGPInfo *info = purple_connection_get_protocol_data(gc);
@@ -394,6 +398,8 @@
 static void ggp_chat_join_id(PurpleConnection *gc, uint64_t id)
 {
 	GHashTable *components;
+	PurpleProtocol *protocol = purple_connection_get_protocol(gc);
+
 	ggp_chat_local_info *chat = ggp_chat_get(gc, id);
 
 	if (chat && !chat->left) {
@@ -416,12 +422,15 @@
 			"participant"), NULL);
 	}
 
-	components = ggp_chat_info_defaults(gc, ggp_chat_get_name_from_id(id));
+	components = ggp_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc,
+	                                    ggp_chat_get_name_from_id(id));
 	purple_serv_got_join_chat_failed(gc, components);
 	g_hash_table_destroy(components);
 }
 
-void ggp_chat_leave(PurpleConnection *gc, int local_id)
+void
+ggp_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+               gint local_id)
 {
 	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	ggp_chat_local_info *chat;
@@ -448,8 +457,9 @@
 	chat->left = TRUE;
 }
 
-void ggp_chat_invite(PurpleConnection *gc, int local_id, const char *message,
-	const char *who)
+void
+ggp_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                gint local_id, const gchar *message, const gchar *who)
 {
 	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	ggp_chat_local_info *chat;
@@ -470,7 +480,9 @@
 	}
 }
 
-int ggp_chat_send(PurpleConnection *gc, int local_id, PurpleMessage *msg)
+gint
+ggp_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+              gint local_id, PurpleMessage *msg)
 {
 	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GDateTime *dt = NULL;
--- a/libpurple/protocols/gg/chat.h	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/gg/chat.h	Mon Jan 11 01:51:14 2021 -0600
@@ -41,15 +41,15 @@
 
 void ggp_chat_got_event(PurpleConnection *gc, const struct gg_event *ev);
 
-GList * ggp_chat_info(PurpleConnection *gc);
-GHashTable * ggp_chat_info_defaults(PurpleConnection *gc,
+GList * ggp_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *gc);
+GHashTable * ggp_chat_info_defaults(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
 	const char *chat_name);
-char * ggp_chat_get_name(GHashTable *components);
-void ggp_chat_join(PurpleConnection *gc, GHashTable *components);
-void ggp_chat_leave(PurpleConnection *gc, int local_id);
-void ggp_chat_invite(PurpleConnection *gc, int local_id, const char *message,
+char * ggp_chat_get_name(PurpleProtocolChat *protocol_chat, GHashTable *components);
+void ggp_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, GHashTable *components);
+void ggp_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int local_id);
+void ggp_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int local_id, const char *message,
 	const char *who);
-int ggp_chat_send(PurpleConnection *gc, int local_id, PurpleMessage *msg);
+int ggp_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int local_id, PurpleMessage *msg);
 
 void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id,
 	const char *message, time_t time, uin_t who);
--- a/libpurple/protocols/irc/irc.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/irc/irc.c	Mon Jan 11 01:51:14 2021 -0600
@@ -41,8 +41,8 @@
 static void irc_login_cb(GObject *source, GAsyncResult *res, gpointer user_data);
 static void irc_close(PurpleConnection *gc);
 static int irc_im_send(PurpleProtocolIM *im, PurpleConnection *gc, PurpleMessage *msg);
-static int irc_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg);
-static void irc_chat_join (PurpleConnection *gc, GHashTable *data);
+static int irc_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, PurpleMessage *msg);
+static void irc_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, GHashTable *data);
 static void irc_read_input_cb(GObject *source, GAsyncResult *res, gpointer data);
 
 static guint irc_nick_hash(const char *nick);
@@ -386,8 +386,8 @@
 	return list;
 }
 
-static GList *irc_chat_join_info(PurpleConnection *gc)
-{
+static GList *
+irc_chat_join_info(PurpleProtocolChat *protocol_chat, PurpleConnection *gc) {
 	GList *m = NULL;
 	PurpleProtocolChatEntry *pce;
 
@@ -406,7 +406,9 @@
 	return m;
 }
 
-static GHashTable *irc_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
+static GHashTable *
+irc_chat_info_defaults(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                       const gchar *chat_name)
 {
 	GHashTable *defaults;
 
@@ -761,7 +763,9 @@
 			irc_read_input_cb, gc);
 }
 
-static void irc_chat_join (PurpleConnection *gc, GHashTable *data)
+static void
+irc_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+              GHashTable *data)
 {
 	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	const char *args[2];
@@ -771,11 +775,14 @@
 	irc_cmd_join(irc, "join", NULL, args);
 }
 
-static char *irc_get_chat_name(GHashTable *data) {
+static gchar *
+irc_get_chat_name(PurpleProtocolChat *protocol_chat, GHashTable *data) {
 	return g_strdup(g_hash_table_lookup(data, "channel"));
 }
 
-static void irc_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
+static void
+irc_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                gint id, const gchar *message, const gchar *name)
 {
 	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
@@ -791,7 +798,9 @@
 }
 
 
-static void irc_chat_leave (PurpleConnection *gc, int id)
+static void
+irc_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+               gint id)
 {
 	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
@@ -806,7 +815,9 @@
 	purple_serv_got_chat_left(gc, id);
 }
 
-static int irc_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg)
+static gint
+irc_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, gint id,
+              PurpleMessage *msg)
 {
 	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
@@ -854,7 +865,9 @@
 	g_free(ib);
 }
 
-static void irc_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
+static void
+irc_chat_set_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                   gint id, const gchar *topic)
 {
 	char *buf;
 	const char *name = NULL;
--- a/libpurple/protocols/jabber/chat.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/jabber/chat.c	Mon Jan 11 01:51:14 2021 -0600
@@ -31,7 +31,9 @@
 #include "xdata.h"
 #include "data.h"
 
-GList *jabber_chat_info(PurpleConnection *gc)
+GList *
+jabber_chat_info(PurpleProtocolChat *protocol_chat,
+                 PurpleConnection *connection)
 {
 	GList *m = NULL;
 	PurpleProtocolChatEntry *pce;
@@ -203,7 +205,8 @@
 
 void jabber_chat_member_free(JabberChatMember *jcm);
 
-char *jabber_get_chat_name(GHashTable *data) {
+gchar *
+jabber_get_chat_name(PurpleProtocolChat *protocol_chat, GHashTable *data) {
 	char *room, *server, *chat_name = NULL;
 
 	room = g_hash_table_lookup(data, "room");
@@ -414,7 +417,9 @@
 	jabber_id_free(jid);
 }
 
-void jabber_chat_leave(PurpleConnection *gc, int id)
+void
+jabber_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                  gint id)
 {
 	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberChat *chat = jabber_chat_find_by_id(js, id);
@@ -454,7 +459,9 @@
 	return purple_chat_conversation_has_user(conv, name);
 }
 
-char *jabber_chat_user_real_name(PurpleConnection *gc, int id, const char *who)
+gchar *
+jabber_chat_user_real_name(PurpleProtocolChat *protocol_chat,
+                           PurpleConnection *gc, gint id, const gchar *who)
 {
 	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberChat *chat;
@@ -736,7 +743,9 @@
 	jabber_message_free(jm);
 }
 
-void jabber_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
+void
+jabber_chat_set_topic(PurpleProtocolChat *protocol_chat,
+                      PurpleConnection *gc, gint id, const gchar *topic)
 {
 	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberChat *chat = jabber_chat_find_by_id(js, id);
--- a/libpurple/protocols/jabber/chat.h	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/jabber/chat.h	Mon Jan 11 01:51:14 2021 -0600
@@ -52,9 +52,9 @@
 	time_t joined;
 } JabberChat;
 
-GList *jabber_chat_info(PurpleConnection *gc);
+GList *jabber_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *connection);
 GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name);
-char *jabber_get_chat_name(GHashTable *data);
+char *jabber_get_chat_name(PurpleProtocolChat *protocol_chat, GHashTable *data);
 
 /**
  * in-protocol function for joining a chat room. Doesn't require sticking goop
@@ -80,13 +80,13 @@
 gboolean jabber_chat_find_buddy(PurpleChatConversation *conv, const char *name);
 void jabber_chat_invite(PurpleConnection *gc, int id, const char *message,
 		const char *name);
-void jabber_chat_leave(PurpleConnection *gc, int id);
-char *jabber_chat_user_real_name(PurpleConnection *gc, int id, const char *who);
+void jabber_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id);
+char *jabber_chat_user_real_name(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, const char *who);
 void jabber_chat_request_room_configure(JabberChat *chat);
 void jabber_chat_create_instant_room(JabberChat *chat);
 void jabber_chat_register(JabberChat *chat);
 void jabber_chat_change_topic(JabberChat *chat, const char *topic);
-void jabber_chat_set_topic(PurpleConnection *gc, int id, const char *topic);
+void jabber_chat_set_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, const char *topic);
 gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick);
 void jabber_chat_part(JabberChat *chat, const char *msg);
 void jabber_chat_track_handle(JabberChat *chat, const char *handle,
--- a/libpurple/protocols/jabber/jabber.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/jabber/jabber.c	Mon Jan 11 01:51:14 2021 -0600
@@ -4193,14 +4193,37 @@
 	im_iface->send_typing = jabber_send_typing;
 }
 
+static GHashTable *
+jabber_protocol_chat_info_defaults(PurpleProtocolChat *protocol_chat,
+                                   PurpleConnection *connection,
+                                   const gchar *name)
+{
+	return jabber_chat_info_defaults(connection, name);
+}
+
+static void
+jabber_protocol_chat_join(PurpleProtocolChat *protocol_chat,
+                          PurpleConnection *connection, GHashTable *components)
+{
+	jabber_chat_join(connection, components);
+}
+
+static void
+jabber_protocol_chat_invite(PurpleProtocolChat *protocol_chat,
+                            PurpleConnection *connection,  gint id,
+                            const gchar *message, const gchar *who)
+{
+	jabber_chat_invite(connection, id, message, who);
+}
+
 static void
 jabber_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
 {
 	chat_iface->info               = jabber_chat_info;
-	chat_iface->info_defaults      = jabber_chat_info_defaults;
-	chat_iface->join               = jabber_chat_join;
+	chat_iface->info_defaults      = jabber_protocol_chat_info_defaults;
+	chat_iface->join               = jabber_protocol_chat_join;
 	chat_iface->get_name           = jabber_get_chat_name;
-	chat_iface->invite             = jabber_chat_invite;
+	chat_iface->invite             = jabber_protocol_chat_invite;
 	chat_iface->leave              = jabber_chat_leave;
 	chat_iface->send               = jabber_message_send_chat;
 	chat_iface->get_user_real_name = jabber_chat_user_real_name;
--- a/libpurple/protocols/jabber/message.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/jabber/message.c	Mon Jan 11 01:51:14 2021 -0600
@@ -1226,7 +1226,9 @@
 	return 1;
 }
 
-int jabber_message_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg)
+gint
+jabber_message_send_chat(PurpleProtocolChat *protocol_chat,
+                         PurpleConnection *gc, gint id, PurpleMessage *msg)
 {
 	JabberChat *chat;
 	JabberMessage *jm;
--- a/libpurple/protocols/jabber/message.h	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/jabber/message.h	Mon Jan 11 01:51:14 2021 -0600
@@ -71,7 +71,7 @@
 
 void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet);
 int jabber_message_send_im(PurpleProtocolIM *pim, PurpleConnection *gc, PurpleMessage *msg);
-int jabber_message_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg);
+int jabber_message_send_chat(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, PurpleMessage *msg);
 
 unsigned int jabber_send_typing(PurpleProtocolIM *pim, PurpleConnection *gc, const char *who, PurpleIMTypingState state);
 
--- a/libpurple/protocols/novell/novell.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/novell/novell.c	Mon Jan 11 01:51:14 2021 -0600
@@ -2405,7 +2405,8 @@
 }
 
 static void
-novell_chat_leave(PurpleConnection * gc, int id)
+novell_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                  gint id)
 {
 	NMConference *conference;
 	NMUser *user;
@@ -2435,8 +2436,8 @@
 }
 
 static void
-novell_chat_invite(PurpleConnection *gc, int id,
-				   const char *message, const char *who)
+novell_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                   gint id, const gchar *message, const gchar *who)
 {
 	NMConference *conference;
 	NMUser *user;
@@ -2472,8 +2473,9 @@
 	}
 }
 
-static int
-novell_chat_send(PurpleConnection * gc, int id, PurpleMessage *msg)
+static gint
+novell_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                 gint id, PurpleMessage *msg)
 {
 	NMConference *conference;
 	PurpleChatConversation *chat;
--- a/libpurple/protocols/null/nullprpl.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/null/nullprpl.c	Mon Jan 11 01:51:14 2021 -0600
@@ -324,7 +324,8 @@
   }
 }
 
-static GList *null_chat_info(PurpleConnection *gc) {
+static GList *
+null_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *gc) {
   PurpleProtocolChatEntry *pce; /* defined in protocols.h */
 
   purple_debug_info("nullprpl", "returning chat setting 'room'\n");
@@ -337,8 +338,10 @@
   return g_list_append(NULL, pce);
 }
 
-static GHashTable *null_chat_info_defaults(PurpleConnection *gc,
-                                               const char *room) {
+static GHashTable *
+null_chat_info_defaults(PurpleProtocolChat *protocol_chat,
+                        PurpleConnection *gc, const gchar *room)
+{
   GHashTable *defaults;
 
   purple_debug_info("nullprpl", "returning chat default setting "
@@ -676,7 +679,10 @@
   }
 }
 
-static void null_join_chat(PurpleConnection *gc, GHashTable *components) {
+static void
+null_join_chat(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+               GHashTable *components)
+{
   const char *username = purple_account_get_username(purple_connection_get_account(gc));
   const char *room = g_hash_table_lookup(components, "room");
   int chat_id = g_str_hash(room);
@@ -699,7 +705,10 @@
   }
 }
 
-static void null_reject_chat(PurpleConnection *gc, GHashTable *components) {
+static void
+null_reject_chat(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                 GHashTable *components)
+{
   const char *invited_by = g_hash_table_lookup(components, "invited_by");
   const char *room = g_hash_table_lookup(components, "room");
   const char *username = purple_account_get_username(purple_connection_get_account(gc));
@@ -722,14 +731,17 @@
   g_free(message);
 }
 
-static char *null_get_chat_name(GHashTable *components) {
+static gchar *
+null_get_chat_name(PurpleProtocolChat *protocol_chat, GHashTable *components) {
   const char *room = g_hash_table_lookup(components, "room");
   purple_debug_info("nullprpl", "reporting chat room name '%s'\n", room);
   return g_strdup(room);
 }
 
-static void null_chat_invite(PurpleConnection *gc, int id,
-                                 const char *message, const char *who) {
+static void
+null_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                 gint id, const gchar *message, const gchar *who)
+{
   const char *username = purple_account_get_username(purple_connection_get_account(gc));
   PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
   const char *room = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
@@ -771,7 +783,10 @@
   }
 }
 
-static void null_chat_leave(PurpleConnection *gc, int id) {
+static void
+null_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                gint id)
+{
   PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
   purple_debug_info("nullprpl", "%s is leaving chat room %s\n",
                     purple_account_get_username(purple_connection_get_account(gc)),
@@ -793,7 +808,10 @@
                    time(NULL));
 }
 
-static int null_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg) {
+static gint
+null_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+               gint id, PurpleMessage *msg)
+{
   const char *username = purple_account_get_username(purple_connection_get_account(gc));
   PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
   const gchar *message = purple_message_get_contents(msg);
@@ -890,8 +908,10 @@
   g_free(msg);
 }
 
-static void null_set_chat_topic(PurpleConnection *gc, int id,
-                                    const char *topic) {
+static void
+null_set_chat_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                    gint id, const gchar *topic)
+{
   PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
   const char *last_topic;
 
--- a/libpurple/protocols/sametime/sametime.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/sametime/sametime.c	Mon Jan 11 01:51:14 2021 -0600
@@ -3491,7 +3491,10 @@
 }
 
 
-static GList *mw_protocol_chat_info(PurpleConnection *gc) {
+static GList *
+mw_protocol_chat_info(PurpleProtocolChat *protocol_chat,
+                      PurpleConnection *gc)
+{
   GList *l = NULL;
   PurpleProtocolChatEntry *pce;
 
@@ -3504,8 +3507,11 @@
 }
 
 
-static GHashTable *mw_protocol_chat_info_defaults(PurpleConnection *gc,
-					      const char *name) {
+static GHashTable *
+mw_protocol_chat_info_defaults(PurpleProtocolChat *protocol_chat,
+                               PurpleConnection *gc,
+                               const char *name)
+{
   GHashTable *table;
 
   g_return_val_if_fail(gc != NULL, NULL);
@@ -4362,8 +4368,10 @@
 }
 
 
-static void mw_protocol_join_chat(PurpleConnection *gc,
-			      GHashTable *components) {
+static void
+mw_protocol_join_chat(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                      GHashTable *components)
+{
 
   struct mwPurpleProtocolData *pd;
   char *c, *t;
@@ -4403,9 +4411,11 @@
 }
 
 
-static void mw_protocol_reject_chat(PurpleConnection *gc,
-				GHashTable *components) {
-
+static void
+mw_protocol_reject_chat(PurpleProtocolChat *protocol_chat,
+                        PurpleConnection *gc,
+                        GHashTable *components)
+{
   struct mwPurpleProtocolData *pd;
   struct mwServiceConference *srvc;
   char *c;
@@ -4427,16 +4437,18 @@
 }
 
 
-static char *mw_protocol_get_chat_name(GHashTable *components) {
+static char *
+mw_protocol_get_chat_name(PurpleProtocolChat *protocol_chat,
+                          GHashTable *components)
+{
   return g_hash_table_lookup(components, CHAT_KEY_NAME);
 }
 
 
-static void mw_protocol_chat_invite(PurpleConnection *gc,
-				int id,
-				const char *invitation,
-				const char *who) {
-
+static void
+mw_protocol_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                        int id, const char *invitation, const char *who)
+{
   struct mwPurpleProtocolData *pd;
   struct mwConference *conf;
   struct mwPlace *place;
@@ -4460,9 +4472,10 @@
 }
 
 
-static void mw_protocol_chat_leave(PurpleConnection *gc,
-			       int id) {
-
+static void
+mw_protocol_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                       int id)
+{
   struct mwPurpleProtocolData *pd;
   struct mwConference *conf;
 
@@ -4483,7 +4496,9 @@
 }
 
 
-static int mw_protocol_chat_send(PurpleConnection *gc, int id, PurpleMessage *pmsg)
+static int
+mw_protocol_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                      int id, PurpleMessage *pmsg)
 {
   struct mwPurpleProtocolData *pd;
   struct mwConference *conf;
--- a/libpurple/protocols/zephyr/zephyr.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/protocols/zephyr/zephyr.c	Mon Jan 11 01:51:14 2021 -0600
@@ -1623,7 +1623,9 @@
 	return sig;
 }
 
-static int zephyr_chat_send(PurpleConnection * gc, int id, PurpleMessage *msg)
+static int
+zephyr_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
+                 int id, PurpleMessage *msg)
 {
 	zephyr_triple *zt;
 	const char *sig;
@@ -1931,8 +1933,8 @@
 	return types;
 }
 
-static GList *zephyr_chat_info(PurpleConnection * gc)
-{
+static GList *
+zephyr_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection * gc) {
 	GList *m = NULL;
 	PurpleProtocolChatEntry *pce;
 
@@ -1966,7 +1968,8 @@
 	g_free(subscribe_failed);
 }
 
-static char *zephyr_get_chat_name(GHashTable *data) {
+static char *
+zephyr_get_chat_name(PurpleProtocolChat *protocol_chat, GHashTable *data) {
 	gchar* zclass = g_hash_table_lookup(data,"class");
 	gchar* inst = g_hash_table_lookup(data,"instance");
 	gchar* recipient = g_hash_table_lookup(data, "recipient");
@@ -2048,7 +2051,16 @@
 	zephyr_chat_set_topic(gc,zt1->id,instname);
 }
 
-static void zephyr_chat_leave(PurpleConnection * gc, int id)
+static void
+zephyr_protocol_chat_join(PurpleProtocolChat *protocol_chat,
+                          PurpleConnection *gc, GHashTable * data)
+{
+	zephyr_join_chat(gc, data);
+}
+
+static void
+zephyr_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection * gc,
+                  int id)
 {
 	zephyr_triple *zt;
 	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
@@ -2163,6 +2175,13 @@
 	g_free(topic_utf8);
 }
 
+static void
+zephyr_protocol_chat_set_topic(PurpleProtocolChat *protocol_chat,
+                               PurpleConnection *gc, int id, const char *topic)
+{
+	zephyr_chat_set_topic(gc, id, topic);
+}
+
 /*  commands */
 
 static PurpleCmdRet zephyr_purple_cmd_msg(PurpleConversation *conv,
@@ -2549,11 +2568,11 @@
 zephyr_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
 {
 	chat_iface->info      = zephyr_chat_info;
-	chat_iface->join      = zephyr_join_chat;
+	chat_iface->join      = zephyr_protocol_chat_join;
 	chat_iface->get_name  = zephyr_get_chat_name;
 	chat_iface->leave     = zephyr_chat_leave;
 	chat_iface->send      = zephyr_chat_send;
-	chat_iface->set_topic = zephyr_chat_set_topic;
+	chat_iface->set_topic = zephyr_protocol_chat_set_topic;
 
 	chat_iface->get_user_real_name = NULL; /* XXX */
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleprotocolchat.c	Mon Jan 11 01:51:14 2021 -0600
@@ -0,0 +1,207 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple 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 "purpleprotocolchat.h"
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_INTERFACE(PurpleProtocolChat, purple_protocol_chat, G_TYPE_INVALID)
+
+static void
+purple_protocol_chat_default_init(PurpleProtocolChatInterface *iface) {
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GList *
+purple_protocol_chat_info(PurpleProtocolChat *protocol_chat,
+                          PurpleConnection *connection)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat), NULL);
+	g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->info != NULL) {
+		return iface->info(protocol_chat, connection);
+	}
+
+	return NULL;
+}
+
+GHashTable *
+purple_protocol_chat_info_defaults(PurpleProtocolChat *protocol_chat,
+                                   PurpleConnection *connection,
+                                   const gchar *chat_name)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat), NULL);
+	g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->info_defaults != NULL) {
+		return iface->info_defaults(protocol_chat, connection, chat_name);
+	}
+
+	return NULL;
+}
+
+void
+purple_protocol_chat_join(PurpleProtocolChat *protocol_chat,
+                          PurpleConnection *connection,
+                          GHashTable *components)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat));
+	g_return_if_fail(PURPLE_IS_CONNECTION(connection));
+	g_return_if_fail(components != NULL);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->join != NULL) {
+		iface->join(protocol_chat, connection, components);
+	}
+}
+
+void
+purple_protocol_chat_reject(PurpleProtocolChat *protocol_chat,
+                            PurpleConnection *connection,
+                            GHashTable *components)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat));
+	g_return_if_fail(PURPLE_IS_CONNECTION(connection));
+	g_return_if_fail(components != NULL);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->reject != NULL) {
+		iface->reject(protocol_chat, connection, components);
+	}
+}
+
+gchar *
+purple_protocol_chat_get_name(PurpleProtocolChat *protocol_chat,
+                              GHashTable *components)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat), NULL);
+	g_return_val_if_fail(components != NULL, NULL);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->get_name != NULL) {
+		return iface->get_name(protocol_chat, components);
+	}
+
+	return NULL;
+}
+
+void
+purple_protocol_chat_invite(PurpleProtocolChat *protocol_chat,
+                            PurpleConnection *connection, gint id,
+                            const gchar *message, const gchar *who)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat));
+	g_return_if_fail(PURPLE_IS_CONNECTION(connection));
+	g_return_if_fail(who != NULL);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->invite != NULL) {
+		iface->invite(protocol_chat, connection, id, message, who);
+	}
+}
+
+void
+purple_protocol_chat_leave(PurpleProtocolChat *protocol_chat,
+                           PurpleConnection *connection, gint id)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat));
+	g_return_if_fail(PURPLE_IS_CONNECTION(connection));
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->leave != NULL) {
+		iface->leave(protocol_chat, connection, id);
+	}
+}
+
+gint
+purple_protocol_chat_send(PurpleProtocolChat *protocol_chat,
+                          PurpleConnection *connection, gint id,
+                          PurpleMessage *message)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat), -1);
+	g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), -1);
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), -1);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->send != NULL) {
+		return iface->send(protocol_chat, connection, id, message);
+	}
+
+	return -1;
+}
+
+gchar *
+purple_protocol_chat_get_user_real_name(PurpleProtocolChat *protocol_chat,
+                                        PurpleConnection *connection, gint id,
+                                        const gchar *who)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat), NULL);
+	g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
+	g_return_val_if_fail(who != NULL, NULL);
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->get_user_real_name != NULL) {
+		return iface->get_user_real_name(protocol_chat, connection, id, who);
+	}
+
+	return NULL;
+}
+
+void
+purple_protocol_chat_set_topic(PurpleProtocolChat *protocol_chat,
+                               PurpleConnection *connection, gint id,
+                               const gchar *topic)
+{
+	PurpleProtocolChatInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL_CHAT(protocol_chat));
+	g_return_if_fail(PURPLE_IS_CONNECTION(connection));
+
+	iface = PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol_chat);
+	if(iface != NULL && iface->set_topic != NULL) {
+		iface->set_topic(protocol_chat, connection, id, topic);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleprotocolchat.h	Mon Jan 11 01:51:14 2021 -0600
@@ -0,0 +1,262 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple 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(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <purple.h> may be included directly"
+#endif
+
+#ifndef PURPLE_PROTOCOL_CHAT_H
+#define PURPLE_PROTOCOL_CHAT_H
+
+/**
+ * SECTION:purpleprotocolchat
+ * @section_id: libpurple-purpleprotocolchat
+ * @short_description: Protocol Chat Interface
+ * @title: ProtocolChat Interface
+ *
+ * #PurpleProtocolChat describes the API that protocols need to implement for
+ * handling multiple user conversations.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libpurple/connection.h>
+#include <libpurple/purplemessage.h>
+
+#define PURPLE_TYPE_PROTOCOL_CHAT (purple_protocol_chat_get_type())
+G_DECLARE_INTERFACE(PurpleProtocolChat, purple_protocol_chat, PURPLE, PROTOCOL_CHAT,
+                    GObject)
+
+G_BEGIN_DECLS
+
+/**
+ * PURPLE_TYPE_PROTOCOL_CHAT:
+ *
+ * The standard _get_type method for #PurpleProtocolChat.
+ *
+ * Since: 3.0.0
+ */
+
+/**
+ * PurpleProtocolChatInterface:
+ * @info: Returns a list of #PurpleProtocolChatEntry structs, which represent
+ *        information required by the protocol to join a chat. libpurple will
+ *        call join_chat along with the information filled by the user.
+ * @info_defaults: Returns a hashtable which maps #PurpleProtocolChatEntry
+ *                 struct identifiers to default options as strings based on
+ *                 @chat_name. The resulting hashtable should be created with
+ *                 #g_hash_table_new_full(#g_str_hash, #g_str_equal, %NULL,
+ *                 #g_free). Use @get_name if you instead need to extract a chat
+ *                 name from a hashtable.
+ * @join: Called when the user requests joining a chat. Should arrange for
+ *        purple_serv_got_joined_chat() to be called.
+ * @reject: Called when the user refuses a chat invitation.
+ * @get_name: Returns a chat name based on the information in components. Use
+ *            @info_defaults if you instead need to generate a hashtable from a
+ *            chat name.
+ * @invite: Invite a user to join a chat.
+ * @leave: Called when the user requests leaving a chat.
+ * @send: Send a message to a chat.
+ * @get_user_real_name: Gets the real name of a participant in a chat. For
+ *                      example, on XMPP this turns a chat room nick
+ *                      <literal>foo</literal> into
+ *                      <literal>room\@server/foo</literal>.
+ * @set_topic: Called to set the topic for the given chat.
+ *
+ * The protocol chat interface.
+ *
+ * This interface provides callbacks needed by protocols that implement chats.
+ */
+struct _PurpleProtocolChatInterface {
+	/*< private >*/
+	GTypeInterface parent;
+
+	/*< public >*/
+	GList *(*info)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection);
+
+	GHashTable *(*info_defaults)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, const gchar *chat_name);
+
+	void (*join)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, GHashTable *components);
+
+	void (*reject)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, GHashTable *components);
+
+	gchar *(*get_name)(PurpleProtocolChat *protocol_chat, GHashTable *components);
+
+	void (*invite)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, const gchar *message, const gchar *who);
+
+	void (*leave)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id);
+
+	gint (*send)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, PurpleMessage *message);
+
+	gchar *(*get_user_real_name)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, const gchar *who);
+
+	void (*set_topic)(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, const gchar *topic);
+
+	/*< private >*/
+	gpointer reserved[8];
+};
+
+/**
+ * purple_protocol_chat_info:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ *
+ * Gets the list of #PurpleProtocolChatEntry's that are required to join a
+ * multi user chat.
+ *
+ * Returns: (transfer full) (element-type PurpleProtocolChatEntry): The list
+ *          of #PurpleProtocolChatEntry's that are used to join a chat.
+ *
+ * Since: 3.0.0
+ */
+GList *purple_protocol_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *connection);
+
+/**
+ * purple_protocol_chat_info_defaults:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @chat_name: The name of the chat.
+ *
+ * Returns a #GHashTable of the default protocol dependent components that will
+ * be passed to purple_protocol_chat_join().
+ *
+ * Returns: (transfer full) (element-type utf8 utf8): The values that will be
+ *          used to join the chat.
+ *
+ * Since: 3.0.0
+ */
+GHashTable *purple_protocol_chat_info_defaults(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, const gchar *chat_name);
+
+/**
+ * purple_protocol_chat_join:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @components: (element-type utf8 utf8) (transfer none): The protocol
+ *              dependent join components.
+ *
+ * Joins the chat described in @components.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, GHashTable *components);
+
+/**
+ * purple_protocol_chat_reject:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @components: (element-type utf8 utf8) (transfer none): The protocol
+ *              dependent join components.
+ *
+ * Used to reject a chat invite.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_chat_reject(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, GHashTable *components);
+
+/**
+ * purple_protocol_chat_get_name:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @components: (element-type utf8 utf8) (transfer none): The protocol
+ *              dependent join components.
+ *
+ * Gets the name from @components.
+ *
+ * Returns: (transfer full): The chat name from @components.
+ *
+ * Since: 3.0.0
+ */
+gchar *purple_protocol_chat_get_name(PurpleProtocolChat *protocol_chat, GHashTable *components);
+
+/**
+ * purple_protocol_chat_invite:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @id: The id of the chat.
+ * @message: The invite message.
+ * @who: The target of the invite.
+ *
+ * Sends an invite to @who with @message.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, const gchar *message, const gchar *who);
+
+/**
+ * purple_protocol_chat_leave:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @id: The id of the chat.
+ *
+ * Leaves the chat identified by @id.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id);
+
+/**
+ * purple_protocol_chat_send:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @id: The id of the chat.
+ * @message: The message to send.
+ *
+ * Sends @message to the chat identified by @id.
+ *
+ * Returns: 0 on success, non-zero on failure.
+ *
+ * Since: 3.0.0
+ */
+gint purple_protocol_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, PurpleMessage *message);
+
+/**
+ * purple_protocol_chat_get_user_real_name:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @id: The id of the chat.
+ * @who: The username.
+ *
+ * Gets the real name of @who.
+ *
+ * Returns: (transfer full): The realname of @who.
+ *
+ * Since: 3.0.0
+ */
+gchar *purple_protocol_chat_get_user_real_name(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, const gchar *who);
+
+/**
+ * purple_protocol_chat_set_topic:
+ * @protocol_chat: The #PurpleProtocolChat instance.
+ * @connection: The #PurpleConnection instance.
+ * @id: The id of the chat.
+ * @topic: The new topic.
+ *
+ * Sets the topic for the chat with id @id to @topic.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_chat_set_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *connection, gint id, const gchar *topic);
+
+G_END_DECLS
+
+#endif /* PURPLE_PROTOCOL_CHAT_H */
+
--- a/libpurple/server.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/libpurple/server.c	Mon Jan 11 01:51:14 2021 -0600
@@ -35,6 +35,7 @@
 #include "protocol.h"
 #include "purpleprivate.h"
 #include "purpleprotocolattention.h"
+#include "purpleprotocolchat.h"
 #include "purpleprotocolim.h"
 #include "purpleprotocolprivacy.h"
 #include "request.h"
@@ -412,7 +413,7 @@
 
 	if (gc) {
 		protocol = purple_connection_get_protocol(gc);
-		purple_protocol_chat_iface_join(protocol, gc, data);
+		purple_protocol_chat_join(PURPLE_PROTOCOL_CHAT(protocol), gc, data);
 	}
 }
 
@@ -423,7 +424,7 @@
 
 	if (gc) {
 		protocol = purple_connection_get_protocol(gc);
-		purple_protocol_chat_iface_reject(protocol, gc, data);
+		purple_protocol_chat_reject(PURPLE_PROTOCOL_CHAT(protocol), gc, data);
 	}
 }
 
@@ -445,8 +446,10 @@
 	purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user",
 					 chat, name, &buffy);
 
-	if (protocol)
-		purple_protocol_chat_iface_invite(protocol, gc, id, buffy, name);
+	if(protocol) {
+		purple_protocol_chat_invite(PURPLE_PROTOCOL_CHAT(protocol), gc, id,
+		                            buffy, name);
+	}
 
 	purple_signal_emit(purple_conversations_get_handle(), "chat-invited-user",
 					 chat, name, buffy);
@@ -464,7 +467,7 @@
 	PurpleProtocol *protocol;
 
 	protocol = purple_connection_get_protocol(gc);
-	purple_protocol_chat_iface_leave(protocol, gc, id);
+	purple_protocol_chat_leave(PURPLE_PROTOCOL_CHAT(protocol), gc, id);
 }
 
 int purple_serv_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg)
@@ -474,8 +477,10 @@
 
 	g_return_val_if_fail(msg != NULL, -EINVAL);
 
-	if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, send))
-		return purple_protocol_chat_iface_send(protocol, gc, id, msg);
+	if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, send)) {
+		return purple_protocol_chat_send(PURPLE_PROTOCOL_CHAT(protocol), gc,
+		                                 id, msg);
+	}
 
 	return -EINVAL;
 }
--- a/pidgin/gtkblist.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/pidgin/gtkblist.c	Mon Jan 11 01:51:14 2021 -0600
@@ -380,8 +380,10 @@
 
 	components = purple_chat_get_components(chat);
 
-	if (protocol)
-		chat_name = purple_protocol_chat_iface_get_name(protocol, components);
+	if(protocol) {
+		chat_name = purple_protocol_chat_get_name(PURPLE_PROTOCOL_CHAT(protocol),
+		                                          components);
+	}
 
 	if (chat_name)
 		name = chat_name;
@@ -651,13 +653,15 @@
 	PurpleRequestField *field;
 	GList *parts, *iter;
 	PurpleProtocolChatEntry *pce;
+	PurpleProtocol *protocol;
 	PurpleConnection *gc;
 	PurpleChat *chat = (PurpleChat*)node;
 
 	purple_request_fields_add_group(fields, group);
 
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	parts = purple_protocol_chat_iface_info(purple_connection_get_protocol(gc), gc);
+	protocol = purple_connection_get_protocol(gc);
+	parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc);
 
 	for (iter = parts; iter; iter = iter->next) {
 		pce = iter->data;
@@ -732,7 +736,8 @@
 		account = purple_chat_get_account(c);
 		protocol = purple_protocols_find(purple_account_get_protocol_id(account));
 		if (protocol) {
-			name = purple_protocol_chat_iface_get_name(protocol, purple_chat_get_components(c));
+			name = purple_protocol_chat_get_name(PURPLE_PROTOCOL_CHAT(protocol),
+			                                     purple_chat_get_components(c));
 		}
 	} else if (PURPLE_IS_CONTACT(node)) {
 		pidgin_log_show_contact(PURPLE_CONTACT(node));
@@ -1056,8 +1061,9 @@
 	g_list_free(data->entries);
 	data->entries = NULL;
 
-	list = purple_protocol_chat_iface_info(protocol, gc);
-	defaults = purple_protocol_chat_iface_info_defaults(protocol, gc, default_chat_name);
+	list = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc);
+	defaults = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol),
+	                                              gc, default_chat_name);
 
 	for (tmp = list; tmp; tmp = tmp->next)
 	{
@@ -3289,10 +3295,12 @@
 			conv = PURPLE_CHAT_CONVERSATION(bnode->conv.conv);
 		} else {
 			char *chat_name;
-			if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, get_name))
-				chat_name = purple_protocol_chat_iface_get_name(protocol, purple_chat_get_components(chat));
-			else
+			if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, get_name)) {
+				chat_name = purple_protocol_chat_get_name(PURPLE_PROTOCOL_CHAT(protocol),
+				                                          purple_chat_get_components(chat));
+			} else {
 				chat_name = g_strdup(purple_chat_get_name(chat));
+			}
 
 			conv = purple_conversations_find_chat_with_account(chat_name,
 					purple_chat_get_account(chat));
@@ -3311,8 +3319,10 @@
 			}
 		}
 
-		if (protocol)
-			cur = purple_protocol_chat_iface_info(protocol, purple_account_get_connection(purple_chat_get_account(chat)));
+		if(protocol) {
+			cur = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol),
+			                                purple_account_get_connection(purple_chat_get_account(chat)));
+		}
 
 		while (cur != NULL)
 		{
--- a/pidgin/gtkconv.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/pidgin/gtkconv.c	Mon Jan 11 01:51:14 2021 -0600
@@ -1114,9 +1114,10 @@
 
 	protocol = purple_connection_get_protocol(gc);
 
-	if (protocol)
-		real_who = purple_protocol_chat_iface_get_user_real_name(protocol, gc,
+	if(protocol) {
+		real_who = purple_protocol_chat_get_user_real_name(PURPLE_PROTOCOL_CHAT(protocol), gc,
 				purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), who);
+	}
 
 	if(!who && !real_who)
 		return;
@@ -1168,9 +1169,10 @@
 
 	protocol = purple_connection_get_protocol(gc);
 
-	if (protocol)
-		real_who = purple_protocol_chat_iface_get_user_real_name(protocol, gc,
+	if(protocol) {
+		real_who = purple_protocol_chat_get_user_real_name(PURPLE_PROTOCOL_CHAT(protocol), gc,
 				purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), who);
+	}
 
 	purple_serv_send_file(gc, real_who ? real_who : who, NULL);
 	g_free(real_who);
@@ -1256,7 +1258,7 @@
 				can_receive_file = FALSE;
 			} else {
 				gchar *real_who = NULL;
-				real_who = purple_protocol_chat_iface_get_user_real_name(protocol, gc,
+				real_who = purple_protocol_chat_get_user_real_name(PURPLE_PROTOCOL_CHAT(protocol), gc,
 					purple_chat_conversation_get_id(chat), who);
 
 				if (!purple_protocol_xfer_can_receive(
@@ -2477,7 +2479,7 @@
 					purple_protocols_find(purple_account_get_protocol_id(account));
 			if (purple_account_get_connection(account) != NULL &&
 					PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info_defaults)) {
-				components = purple_protocol_chat_iface_info_defaults(protocol, purple_account_get_connection(account),
+				components = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), purple_account_get_connection(account),
 						purple_conversation_get_name(conv));
 			} else {
 				components = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -3298,7 +3300,6 @@
 	PurpleConversation *conv;
 	PidginChatPane *gtkchat;
 	PurpleConnection *gc;
-	PurpleProtocol *protocol;
 	GtkTreeModel *tm;
 	GtkListStore *ls;
 	GtkTreePath *newpath;
@@ -3417,7 +3418,7 @@
 	else
 		gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), "");
 
-	purple_protocol_chat_iface_set_topic(protocol, gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)),
+	purple_protocol_chat_set_topic(PURPLE_PROTOCOL_CHAT(protocol), gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)),
 			new_topic);
 
 	g_free(new_topic);
@@ -5730,7 +5731,7 @@
 			PurpleChat *chat = purple_blist_find_chat(purple_conversation_get_account(conv), purple_conversation_get_name(conv));
 			if (chat == NULL) {
 				PurpleProtocol *protocol = purple_connection_get_protocol(gc);
-				comps = purple_protocol_chat_iface_info_defaults(protocol, gc, purple_conversation_get_name(conv));
+				comps = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc, purple_conversation_get_name(conv));
 			} else {
 				comps = purple_chat_get_components(chat);
 			}
--- a/pidgin/gtkutils.c	Mon Jan 11 01:08:47 2021 -0600
+++ b/pidgin/gtkutils.c	Mon Jan 11 01:51:14 2021 -0600
@@ -421,7 +421,7 @@
 
 	protocol = purple_connection_get_protocol(conn);
 	if (protocol != NULL)
-		who = purple_protocol_chat_iface_get_user_real_name(protocol, conn, chat, name);
+		who = purple_protocol_chat_get_user_real_name(PURPLE_PROTOCOL_CHAT(protocol), conn, chat, name);
 
 	pidgin_retrieve_user_info(conn, who ? who : name);
 	g_free(who);
--- a/po/POTFILES.in	Mon Jan 11 01:08:47 2021 -0600
+++ b/po/POTFILES.in	Mon Jan 11 01:51:14 2021 -0600
@@ -257,6 +257,7 @@
 libpurple/purplemessage.c
 libpurple/purplepresence.c
 libpurple/purpleprotocolattention.c
+libpurple/purpleprotocolchat.c
 libpurple/purpleprotocolclient.c
 libpurple/purpleprotocolim.c
 libpurple/purpleprotocolmedia.c

mercurial