Move PurpleProtocolMediaInterface to its own file.

Thu, 27 Aug 2020 03:29:49 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Thu, 27 Aug 2020 03:29:49 -0500
changeset 40525
96fc115d6c36
parent 40524
a3b1cde8d2d8
child 40526
c8cc1a4c4a02

Move PurpleProtocolMediaInterface to its own file.

Also cleaned up a few minor things along the way.

Testing Done:
Compile, docs, didn't update the pot file, but did a voice call between pidgin2 and pidgin3.

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

doc/reference/libpurple/libpurple-docs.xml 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.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/jabber.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/jabber.h file | annotate | diff | comparison | revisions
libpurple/purpleprotocolim.h file | annotate | diff | comparison | revisions
libpurple/purpleprotocolmedia.c file | annotate | diff | comparison | revisions
libpurple/purpleprotocolmedia.h file | annotate | diff | comparison | revisions
po/POTFILES.in file | annotate | diff | comparison | revisions
--- a/doc/reference/libpurple/libpurple-docs.xml	Tue Aug 25 23:18:09 2020 -0500
+++ b/doc/reference/libpurple/libpurple-docs.xml	Thu Aug 27 03:29:49 2020 -0500
@@ -71,6 +71,7 @@
       <xi:include href="xml/purplekeyvaluepair.xml" />
       <xi:include href="xml/purpleprotocolfactory.xml" />
       <xi:include href="xml/purpleprotocolim.xml" />
+      <xi:include href="xml/purpleprotocolmedia.xml" />
       <xi:include href="xml/purpleprotocolprivacy.xml" />
       <xi:include href="xml/purpleuiinfo.xml" />
       <xi:include href="xml/queuedoutputstream.xml" />
--- a/libpurple/meson.build	Tue Aug 25 23:18:09 2020 -0500
+++ b/libpurple/meson.build	Thu Aug 27 03:29:49 2020 -0500
@@ -55,6 +55,7 @@
 	'purplekeyvaluepair.c',
 	'purpleprotocolfactory.c',
 	'purpleprotocolim.c',
+	'purpleprotocolmedia.c',
 	'purpleprotocolprivacy.c',
 	'purpleuiinfo.c',
 	'queuedoutputstream.c',
@@ -135,6 +136,7 @@
 	'purplekeyvaluepair.h',
 	'purpleprotocolfactory.h',
 	'purpleprotocolim.h',
+	'purpleprotocolmedia.h',
 	'purpleprotocolprivacy.h',
 	'purpleuiinfo.h',
 	'queuedoutputstream.h',
--- a/libpurple/protocol.c	Tue Aug 25 23:18:09 2020 -0500
+++ b/libpurple/protocol.c	Thu Aug 27 03:29:49 2020 -0500
@@ -638,61 +638,3 @@
 
 #undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
 #undef DEFINE_PROTOCOL_FUNC
-
-/**************************************************************************
- * Protocol Media Interface API
- **************************************************************************/
-#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
-	PurpleProtocolMediaInterface *media_iface = \
-		PURPLE_PROTOCOL_MEDIA_GET_IFACE(protocol); \
-	if (media_iface && media_iface->funcname) \
-		media_iface->funcname(__VA_ARGS__);
-
-#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
-	PurpleProtocolMediaInterface *media_iface = \
-		PURPLE_PROTOCOL_MEDIA_GET_IFACE(protocol); \
-	if (media_iface && media_iface->funcname) \
-		return media_iface->funcname(__VA_ARGS__); \
-	else \
-		return defaultreturn;
-
-GType
-purple_protocol_media_iface_get_type(void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY(type == 0)) {
-		static const GTypeInfo info = {
-			.class_size = sizeof(PurpleProtocolMediaInterface),
-		};
-
-		type = g_type_register_static(G_TYPE_INTERFACE,
-				"PurpleProtocolMediaInterface", &info, 0);
-	}
-	return type;
-}
-
-gboolean
-purple_protocol_media_iface_initiate_session(PurpleProtocol *protocol,
-		PurpleAccount *account, const char *who, PurpleMediaSessionType type)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, FALSE, initiate_session, account,
-			who, type);
-}
-
-PurpleMediaCaps
-purple_protocol_media_iface_get_caps(PurpleProtocol *protocol,
-		PurpleAccount *account, const char *who)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, get_caps, account, who);
-}
-
-gboolean purple_protocol_media_iface_send_dtmf(PurpleProtocol *protocol,
-		PurpleMedia *media, gchar dtmf, guint8 volume, guint8 duration)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, FALSE, send_dtmf, media,
-			dtmf, volume, duration);
-}
-
-#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
-#undef DEFINE_PROTOCOL_FUNC
--- a/libpurple/protocol.h	Tue Aug 25 23:18:09 2020 -0500
+++ b/libpurple/protocol.h	Thu Aug 27 03:29:49 2020 -0500
@@ -474,54 +474,6 @@
 #define PURPLE_PROTOCOL_CHAT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CHAT, \
                                              PurpleProtocolChatInterface))
 
-
-#define PURPLE_TYPE_PROTOCOL_MEDIA (purple_protocol_media_iface_get_type())
-
-typedef struct _PurpleProtocolMediaInterface PurpleProtocolMediaInterface;
-
-/**
- * PurpleProtocolMediaInterface:
- * @initiate_session: Initiate a media session with the given contact.
- *                    <sbr/>@account: The account to initiate the media session
- *                                    on.
- *                    <sbr/>@who: The remote user to initiate the session with.
- *                    <sbr/>@type: The type of media session to initiate.
- *                    <sbr/>Returns: %TRUE if the call succeeded else %FALSE.
- *                                   (Doesn't imply the media session or stream
- *                                   will be successfully created)
- * @get_caps: Checks to see if the given contact supports the given type of
- *            media session.
- *            <sbr/>@account: The account the contact is on.
- *            <sbr/>@who: The remote user to check for media capability with.
- *            <sbr/>Returns: The media caps the contact supports.
- * @send_dtmf: Sends DTMF codes out-of-band in a protocol-specific way if the
- *             protocol supports it, or failing that in-band if the media backend
- *             can do so. See purple_media_send_dtmf().
- *
- * The protocol media interface.
- *
- * This interface provides callbacks for media sessions on the protocol.
- */
-struct _PurpleProtocolMediaInterface
-{
-	/*< private >*/
-	GTypeInterface parent_iface;
-
-	/*< public >*/
-	gboolean (*initiate_session)(PurpleAccount *account, const char *who,
-					PurpleMediaSessionType type);
-
-	PurpleMediaCaps (*get_caps)(PurpleAccount *account,
-					  const char *who);
-
-	gboolean (*send_dtmf)(PurpleMedia *media, gchar dtmf,
-				    guint8 volume, guint8 duration);
-};
-
-#define PURPLE_IS_PROTOCOL_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_MEDIA))
-#define PURPLE_PROTOCOL_MEDIA_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_MEDIA, \
-                                              PurpleProtocolMediaInterface))
-
 /**
  * PURPLE_PROTOCOL_IMPLEMENTS:
  * @protocol: The protocol in which to check
@@ -988,71 +940,6 @@
 void purple_protocol_chat_iface_set_topic(PurpleProtocol *protocol,
 		PurpleConnection *gc, int id, const char *topic);
 
-/**************************************************************************/
-/* Protocol Media Interface API                                           */
-/**************************************************************************/
-
-/**
- * purple_protocol_media_iface_get_type:
- *
- * Returns: The #GType for the protocol media interface.
- *
- * Since: 3.0.0
- */
-GType purple_protocol_media_iface_get_type(void);
-
-/**
- * purple_protocol_media_iface_initiate_session:
- * @protocol: The #PurpleProtocol instance.
- * @account: The #PurpleAccount instance.
- * @who: The user to initiate a media session with.
- * @type: The type of media session to create.
- *
- * Initiates a media connection of @type to @who.
- *
- * Returns: TRUE if successful, FALSE otherwise.
- *
- * Since: 3.0.0
- */
-gboolean purple_protocol_media_iface_initiate_session(PurpleProtocol *protocol,
-		PurpleAccount *account, const char *who, PurpleMediaSessionType type);
-
-/**
- * purple_protocol_media_iface_get_caps:
- * @protocol: The #PurpleProtocol instance.
- * @account: The #PurpleAccount instance.
- * @who: The user to get the media capabilites for.
- *
- * Gets the #PurpleMediaCaps for @who which determine what types of media are
- * available.
- *
- * Returns: the media capabilities of @who.
- *
- * Since: 3.0.0
- */
-PurpleMediaCaps purple_protocol_media_iface_get_caps(PurpleProtocol *protocol,
-		PurpleAccount *account, const char *who);
-
-/**
- * purple_protocol_media_iface_send_dtmf:
- * @protocol: The #PurpleProtocol instance.
- * @media: The #PurpleMedia instance.
- * @dtmf: A DTMF to send.
- * @volume: The volume to send @dtmf at.
- * @duration: The duration to send @dtmf (in ms?)
- *
- * Sends a DTMF (dual-tone multi-frequency) signal via the established @media
- * for the given @duration at the given @volume.
- *
- * It is up to the specific implementation if DTMF is send in or out of band.
- *
- * Returns: TRUE if successful, FALSE otherwise.
- *
- * Since: 3.0.0
- */
-gboolean purple_protocol_media_iface_send_dtmf(PurpleProtocol *protocol,
-		PurpleMedia *media, gchar dtmf, guint8 volume, guint8 duration);
-
 G_END_DECLS
 
 #endif /* PURPLE_PROTOCOL_H */
--- a/libpurple/protocols.c	Tue Aug 25 23:18:09 2020 -0500
+++ b/libpurple/protocols.c	Thu Aug 27 03:29:49 2020 -0500
@@ -30,6 +30,7 @@
 #include "notify.h"
 #include "protocol.h"
 #include "purpleaccountoption.h"
+#include "purpleprotocolmedia.h"
 #include "request.h"
 #include "util.h"
 
@@ -469,17 +470,24 @@
 	PurpleConnection *gc = NULL;
 	PurpleProtocol *protocol = NULL;
 
-	if (account)
+	if(account) {
 		gc = purple_account_get_connection(account);
-	if (gc)
+	}
+	if(gc) {
 		protocol = purple_connection_get_protocol(gc);
+	}
 
-	if (protocol) {
-		/* should check that the protocol supports this media type here? */
-		return purple_protocol_media_iface_initiate_session(protocol, account, who, type);
-	} else
+	if(PURPLE_IS_PROTOCOL_MEDIA(protocol)) {
+		PurpleProtocolMedia *media = PURPLE_PROTOCOL_MEDIA(protocol);
+
+		return purple_protocol_media_initiate_session(media, account, who,
+		                                              type);
+	} else {
+		return FALSE;
+	}
+#else
+	return FALSE;
 #endif
-	return FALSE;
 }
 
 PurpleMediaCaps
@@ -489,15 +497,22 @@
 	PurpleConnection *gc = NULL;
 	PurpleProtocol *protocol = NULL;
 
-	if (account)
+	if(account) {
 		gc = purple_account_get_connection(account);
-	if (gc)
+	}
+	if(gc) {
 		protocol = purple_connection_get_protocol(gc);
+	}
 
-	if (protocol)
-		return purple_protocol_media_iface_get_caps(protocol, account, who);
+	if(PURPLE_IS_PROTOCOL_MEDIA(protocol)) {
+		return purple_protocol_media_get_caps(PURPLE_PROTOCOL_MEDIA(protocol),
+		                                      account, who);
+	} else {
+		return PURPLE_MEDIA_CAPS_NONE;
+	}
+#else
+	return PURPLE_MEDIA_CAPS_NONE;
 #endif
-	return PURPLE_MEDIA_CAPS_NONE;
 }
 
 void
--- a/libpurple/protocols/jabber/jabber.c	Tue Aug 25 23:18:09 2020 -0500
+++ b/libpurple/protocols/jabber/jabber.c	Thu Aug 27 03:29:49 2020 -0500
@@ -3250,6 +3250,7 @@
 }
 
 typedef struct {
+	PurpleProtocolMedia *media;
 	PurpleAccount *account;
 	gchar *who;
 	PurpleMediaSessionType type;
@@ -3258,7 +3259,7 @@
 
 static void
 jabber_media_cancel_cb(JabberMediaRequest *request,
-		PurpleRequestFields *fields)
+                       PurpleRequestFields *fields)
 {
 	g_free(request->who);
 	g_free(request);
@@ -3271,7 +3272,7 @@
 			purple_request_fields_get_field(fields, "resource");
 	const gchar *selected = purple_request_field_choice_get_value(field);
 	gchar *who = g_strdup_printf("%s/%s", request->who, selected);
-	jabber_initiate_media(request->account, who, request->type);
+	jabber_initiate_media(request->media, request->account, who, request->type);
 
 	g_free(who);
 	g_free(request->who);
@@ -3280,8 +3281,8 @@
 #endif
 
 gboolean
-jabber_initiate_media(PurpleAccount *account, const char *who,
-		      PurpleMediaSessionType type)
+jabber_initiate_media(PurpleProtocolMedia *media, PurpleAccount *account,
+                      const gchar *who, PurpleMediaSessionType type)
 {
 #ifdef USE_VV
 	PurpleConnection *gc = purple_account_get_connection(account);
@@ -3342,7 +3343,7 @@
 		gboolean result;
 		jbr = jb->resources->data;
 		name = g_strdup_printf("%s/%s", who, jbr->name);
-		result = jabber_initiate_media(account, name, type);
+		result = jabber_initiate_media(media, account, name, type);
 		g_free(name);
 		return result;
 	} else {
@@ -3364,7 +3365,7 @@
 			PurpleMediaCaps caps;
 			gchar *name;
 			name = g_strdup_printf("%s/%s", who, ljbr->name);
-			caps = jabber_get_media_caps(account, name);
+			caps = jabber_get_media_caps(media, account, name);
 			g_free(name);
 
 			if ((type & PURPLE_MEDIA_AUDIO) &&
@@ -3399,7 +3400,7 @@
 			gboolean result;
 			purple_request_field_destroy(field);
 			name = g_strdup_printf("%s/%s", who, jbr->name);
-			result = jabber_initiate_media(account, name, type);
+			result = jabber_initiate_media(media, account, name, type);
 			g_free(name);
 			return result;
 		}
@@ -3408,6 +3409,7 @@
 		fields = purple_request_fields_new();
 		group =	purple_request_field_group_new(NULL);
 		request = g_new0(JabberMediaRequest, 1);
+		request->media = media;
 		request->account = account;
 		request->who = g_strdup(who);
 		request->type = type;
@@ -3428,7 +3430,9 @@
 	return FALSE;
 }
 
-PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who)
+PurpleMediaCaps
+jabber_get_media_caps(PurpleProtocolMedia *media, PurpleAccount *account,
+                      const gchar *who)
 {
 #ifdef USE_VV
 	PurpleConnection *gc = purple_account_get_connection(account);
--- a/libpurple/protocols/jabber/jabber.h	Tue Aug 25 23:18:09 2020 -0500
+++ b/libpurple/protocols/jabber/jabber.h	Thu Aug 27 03:29:49 2020 -0500
@@ -423,9 +423,8 @@
 
 gboolean jabber_audio_enabled(JabberStream *js, const char *unused);
 gboolean jabber_video_enabled(JabberStream *js, const char *unused);
-gboolean jabber_initiate_media(PurpleAccount *account, const char *who,
-		PurpleMediaSessionType type);
-PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who);
+gboolean jabber_initiate_media(PurpleProtocolMedia *media, PurpleAccount *account, const char *who, PurpleMediaSessionType type);
+PurpleMediaCaps jabber_get_media_caps(PurpleProtocolMedia *media, PurpleAccount *account, const char *who);
 gboolean jabber_can_receive_file(PurpleProtocolXfer *xfer, PurpleConnection *gc, const gchar *who);
 
 #endif /* PURPLE_JABBER_JABBER_H */
--- a/libpurple/purpleprotocolim.h	Tue Aug 25 23:18:09 2020 -0500
+++ b/libpurple/purpleprotocolim.h	Thu Aug 27 03:29:49 2020 -0500
@@ -27,8 +27,8 @@
 #define PURPLE_PROTOCOL_IM_H
 
 /**
- * SECTION:protocolim
- * @section_id: libpurple-protocolim
+ * SECTION:purpleprotocolim
+ * @section_id: libpurple-purpleprotocolim
  * @short_description: Protocol Instant Message Interface
  * @title: ProtocolIM Interface
  */
@@ -47,6 +47,8 @@
  * PURPLE_TYPE_PROTOCOL_IM:
  *
  * The standard _get_type method for #PurpleProtocolIM.
+ *
+ * Since: 3.0.0
  */
 
 /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleprotocolmedia.c	Thu Aug 27 03:29:49 2020 -0500
@@ -0,0 +1,85 @@
+/*
+ * purple
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "purpleprotocolmedia.h"
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_INTERFACE(PurpleProtocolMedia, purple_protocol_media, G_TYPE_INVALID)
+
+static void
+purple_protocol_media_default_init(PurpleProtocolMediaInterface *iface) {
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+gboolean
+purple_protocol_media_initiate_session(PurpleProtocolMedia *media,
+                                       PurpleAccount *account,
+                                       const gchar *who,
+                                       PurpleMediaSessionType type)
+{
+	PurpleProtocolMediaInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_MEDIA(media), FALSE);
+
+	iface = PURPLE_PROTOCOL_MEDIA_GET_IFACE(media);
+	if(iface && iface->initiate_session) {
+		return iface->initiate_session(media, account, who, type);
+	}
+
+	return FALSE;
+}
+
+PurpleMediaCaps
+purple_protocol_media_get_caps(PurpleProtocolMedia *media,
+                               PurpleAccount *account, const gchar *who)
+{
+	PurpleProtocolMediaInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_MEDIA(media), PURPLE_MEDIA_NONE);
+
+	iface = PURPLE_PROTOCOL_MEDIA_GET_IFACE(media);
+	if(iface && iface->get_caps) {
+		return iface->get_caps(media, account, who);
+	}
+
+	return PURPLE_MEDIA_NONE;
+}
+
+gboolean
+purple_protocol_media_send_dtmf(PurpleProtocolMedia *protocol_media,
+                                PurpleMedia *media, gchar dtmf, guint8 volume,
+                                guint8 duration)
+{
+	PurpleProtocolMediaInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_MEDIA(protocol_media), FALSE);
+
+	iface = PURPLE_PROTOCOL_MEDIA_GET_IFACE(protocol_media);
+	if(iface && iface->send_dtmf) {
+		return iface->send_dtmf(protocol_media, media, dtmf, volume, duration);
+	}
+
+	return FALSE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleprotocolmedia.h	Thu Aug 27 03:29:49 2020 -0500
@@ -0,0 +1,145 @@
+/*
+ * purple
+ *
+ * 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 <http://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_MEDIA_H
+#define PURPLE_PROTOCOL_MEDIA_H
+
+/**
+ * SECTION:purpleprotocolmedia
+ * @section_id: libpurple-purpleprotocolmedia
+ * @short_description: Protocol Media Interface
+ * @title: ProtocolMedia Interface
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libpurple/account.h>
+#include <libpurple/media.h>
+
+#define PURPLE_TYPE_PROTOCOL_MEDIA (purple_protocol_media_get_type())
+G_DECLARE_INTERFACE(PurpleProtocolMedia, purple_protocol_media, PURPLE,
+                    PROTOCOL_MEDIA, GObject)
+
+G_BEGIN_DECLS
+
+/**
+ * PURPLE_TYPE_PROTOCOL_MEDIA:
+ *
+ * The standard _get_type method for #PurpleProtocolMedia.
+ *
+ * Since: 3.0.0
+ */
+
+/**
+ * PurpleProtocolMediaInterface:
+ * @initiate_session: Initiate a media session with the given contact.
+ *                    <sbr/>@account: The account to initiate the media session
+ *                                    on.
+ *                    <sbr/>@who: The remote user to initiate the session with.
+ *                    <sbr/>@type: The type of media session to initiate.
+ *                    <sbr/>Returns: %TRUE if the call succeeded else %FALSE.
+ *                                   (Doesn't imply the media session or stream
+ *                                   will be successfully created)
+ * @get_caps: Checks to see if the given contact supports the given type of
+ *            media session.
+ *            <sbr/>@account: The account the contact is on.
+ *            <sbr/>@who: The remote user to check for media capability with.
+ *            <sbr/>Returns: The media caps the contact supports.
+ * @send_dtmf: Sends DTMF codes out-of-band in a protocol-specific way if the
+ *             protocol supports it, or failing that in-band if the media backend
+ *             can do so. See purple_media_send_dtmf().
+ *
+ * The protocol media interface.
+ *
+ * This interface provides callbacks for media sessions on the protocol.
+ */
+struct _PurpleProtocolMediaInterface {
+	/*< private >*/
+	GTypeInterface parent;
+
+	/*< public >*/
+	gboolean (*initiate_session)(PurpleProtocolMedia *media, PurpleAccount *account, const gchar *who, PurpleMediaSessionType type);
+
+	PurpleMediaCaps (*get_caps)(PurpleProtocolMedia *media, PurpleAccount *account, const gchar *who);
+
+	gboolean (*send_dtmf)(PurpleProtocolMedia *protocol_media, PurpleMedia *media, gchar dtmf, guint8 volume, guint8 duration);
+
+	/*< private >*/
+	gpointer reserved[4];
+};
+
+/**
+ * purple_protocol_media_initiate_session:
+ * @media: The #PurpleProtocolMedia instance.
+ * @account: The #PurpleAccount instance.
+ * @who: The user to initiate a media session with.
+ * @type: The type of media session to create.
+ *
+ * Initiates a media connection of @type to @who.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_protocol_media_initiate_session(PurpleProtocolMedia *media, PurpleAccount *account, const gchar *who, PurpleMediaSessionType type);
+
+/**
+ * purple_protocol_media_get_caps:
+ * @media: The #PurpleProtocolMedia instance.
+ * @account: The #PurpleAccount instance.
+ * @who: The user to get the media capabilites for.
+ *
+ * Gets the #PurpleMediaCaps for @who which determine what types of media are
+ * available.
+ *
+ * Returns: the media capabilities of @who.
+ *
+ * Since: 3.0.0
+ */
+PurpleMediaCaps purple_protocol_media_get_caps(PurpleProtocolMedia *media, PurpleAccount *account, const gchar *who);
+
+/**
+ * purple_protocol_media_send_dtmf:
+ * @protocol_media: The #PurpleProtocolMedia instance.
+ * @media: The #PurpleMedia instance.
+ * @dtmf: A DTMF to send.
+ * @volume: The volume to send @dtmf at.
+ * @duration: The duration to send @dtmf (in ms?)
+ *
+ * Sends a DTMF (dual-tone multi-frequency) signal via the established @media
+ * for the given @duration at the given @volume.
+ *
+ * It is up to the specific implementation if DTMF is send in or out of band.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_protocol_media_send_dtmf(PurpleProtocolMedia *protocol_media, PurpleMedia *media, gchar dtmf, guint8 volume, guint8 duration);
+
+G_END_DECLS
+
+#endif /* PURPLE_PROTOCOL_MEDIA_H */
--- a/po/POTFILES.in	Tue Aug 25 23:18:09 2020 -0500
+++ b/po/POTFILES.in	Thu Aug 27 03:29:49 2020 -0500
@@ -268,6 +268,8 @@
 libpurple/purple-gio.c
 libpurple/purplechatuser.c
 libpurple/purpleimconversation.c
+libpurple/purpleprotocolim.c
+libpurple/purpleprotocolmedia.c
 libpurple/purpleprotocolprivacy.c
 libpurple/queuedoutputstream.c
 libpurple/request.c

mercurial