pidgin/pidgin

96fc115d6c36
Parents a3b1cde8d2d8
Children 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/
--- 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