Add support for message attachments
Add a PurpleAttachment for storing message attachments
A start to implementing the attachment api in PidginMessage
Add PidginAttachment that wraps PurpleAttachment.
Finish implementing the attachment management api in PidginMessage
Add PidginAttachment to the docs
Testing Done:
Compiled and ran. Messaged with bonjour.
Reviewed at https://reviews.imfreedom.org/r/76/
--- a/doc/reference/libpurple/libpurple-docs.xml Thu Aug 27 03:29:49 2020 -0500
+++ b/doc/reference/libpurple/libpurple-docs.xml Thu Sep 03 20:16:32 2020 -0500
@@ -102,6 +102,7 @@
<xi:include href="xml/message.xml" />
+ <xi:include href="xml/purpleattachment.xml" /> <xi:include href="xml/log.xml" />
<xi:include href="xml/cmds.xml" />
--- a/doc/reference/pidgin/pidgin-docs.xml Thu Aug 27 03:29:49 2020 -0500
+++ b/doc/reference/pidgin/pidgin-docs.xml Thu Sep 03 20:16:32 2020 -0500
@@ -58,6 +58,7 @@
<xi:include href="xml/pidginaccountactionsmenu.xml" />
<xi:include href="xml/pidginaccountsmenu.xml" />
<xi:include href="xml/pidginactiongroup.xml" />
+ <xi:include href="xml/pidginattachment.xml" /> <xi:include href="xml/pidginbuddylistmenu.xml" />
<xi:include href="xml/pidgincontactcompletion.xml" />
<xi:include href="xml/pidgindebug.xml" />
--- a/libpurple/conversation.h Thu Aug 27 03:29:49 2020 -0500
+++ b/libpurple/conversation.h Thu Sep 03 20:16:32 2020 -0500
@@ -110,50 +110,6 @@
} PurpleConversationUpdateType;
- * @PURPLE_MESSAGE_SEND: Outgoing message.
- * @PURPLE_MESSAGE_RECV: Incoming message.
- * @PURPLE_MESSAGE_SYSTEM: System message.
- * @PURPLE_MESSAGE_AUTO_RESP: Auto response.
- * @PURPLE_MESSAGE_ACTIVE_ONLY: Hint to the UI that this message should not be
- * shown in conversations which are only open for
- * internal UI purposes (e.g. for contact-aware
- * @PURPLE_MESSAGE_NICK: Contains your nick.
- * @PURPLE_MESSAGE_NO_LOG: Do not log.
- * @PURPLE_MESSAGE_ERROR: Error message.
- * @PURPLE_MESSAGE_DELAYED: Delayed message.
- * @PURPLE_MESSAGE_RAW: "Raw" message - don't apply formatting
- * @PURPLE_MESSAGE_IMAGES: Message contains images
- * @PURPLE_MESSAGE_NOTIFY: Message is a notification
- * @PURPLE_MESSAGE_NO_LINKIFY: Message should not be auto-linkified
- * @PURPLE_MESSAGE_INVISIBLE: Message should not be displayed
- * @PURPLE_MESSAGE_REMOTE_SEND: Message sent from another location,
- * not an echo of a local one
- * Flags applicable to a message. Most will have send, recv or system.
-typedef enum /*< flags >*/
- PURPLE_MESSAGE_SEND = 1 << 0,
- PURPLE_MESSAGE_RECV = 1 << 1,
- PURPLE_MESSAGE_SYSTEM = 1 << 2,
- PURPLE_MESSAGE_AUTO_RESP = 1 << 3,
- PURPLE_MESSAGE_ACTIVE_ONLY = 1 << 4,
- PURPLE_MESSAGE_NICK = 1 << 5,
- PURPLE_MESSAGE_NO_LOG = 1 << 6,
- PURPLE_MESSAGE_ERROR = 1 << 7,
- PURPLE_MESSAGE_DELAYED = 1 << 8,
- PURPLE_MESSAGE_RAW = 1 << 9,
- PURPLE_MESSAGE_IMAGES = 1 << 10,
- PURPLE_MESSAGE_NOTIFY = 1 << 11,
- PURPLE_MESSAGE_NO_LINKIFY = 1 << 12,
- PURPLE_MESSAGE_INVISIBLE = 1 << 13,
- PURPLE_MESSAGE_REMOTE_SEND = 1 << 14,
--- a/libpurple/meson.build Thu Aug 27 03:29:49 2020 -0500
+++ b/libpurple/meson.build Thu Sep 03 20:16:32 2020 -0500
@@ -52,6 +52,7 @@
'purpleaccountusersplit.c',
'purpleimconversation.c',
'purpleprotocolfactory.c',
@@ -133,6 +134,7 @@
'purpleaccountusersplit.h',
'purpleimconversation.h',
'purpleprotocolfactory.h',
@@ -206,6 +208,7 @@
--- a/libpurple/message.c Thu Aug 27 03:29:49 2020 -0500
+++ b/libpurple/message.c Thu Sep 03 20:16:32 2020 -0500
@@ -46,6 +46,8 @@
PurpleMessageFlags flags;
+ GHashTable *attachments; @@ -246,6 +248,80 @@
+purple_message_add_attachment(PurpleMessage *message, + PurpleAttachment *attachment) + PurpleMessagePrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE); + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), FALSE); + priv = purple_message_get_instance_private(message); + return g_hash_table_insert(priv->attachments, + purple_attachment_get_hash_key(attachment), + g_object_ref(G_OBJECT(attachment))); +purple_message_remove_attachment(PurpleMessage *message, guint64 id) { + PurpleMessagePrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE); + priv = purple_message_get_instance_private(message); + return g_hash_table_remove(priv->attachments, &id); +purple_message_get_attachment(PurpleMessage *message, guint64 id) { + PurpleMessagePrivate *priv = NULL; + PurpleAttachment *attachment = NULL; + g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL); + priv = purple_message_get_instance_private(message); + attachment = g_hash_table_lookup(priv->attachments, &id); + if(PURPLE_IS_ATTACHMENT(attachment)) { + return PURPLE_ATTACHMENT(g_object_ref(G_OBJECT(attachment))); +purple_message_foreach_attachment(PurpleMessage *message, + PurpleAttachmentForeachFunc func, + PurpleMessagePrivate *priv = NULL; + g_return_if_fail(PURPLE_IS_MESSAGE(message)); + g_return_if_fail(func != NULL); + g_hash_table_iter_init(&iter, priv->attachments); + while(g_hash_table_iter_next(&iter, NULL, &value)) { + func(PURPLE_ATTACHMENT(value), data); +purple_message_clear_attachments(PurpleMessage *message) { + PurpleMessagePrivate *priv = NULL; + g_return_if_fail(PURPLE_IS_MESSAGE(message)); + priv = purple_message_get_instance_private(message); + g_hash_table_remove_all(priv->attachments); /******************************************************************************
******************************************************************************/
@@ -257,6 +333,9 @@
PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
+ priv->attachments = g_hash_table_new_full(g_int64_hash, g_int64_equal, g_hash_table_insert(messages, GINT_TO_POINTER(max_id), msg);
@@ -272,6 +351,8 @@
+ g_hash_table_destroy(priv->attachments); G_OBJECT_CLASS(purple_message_parent_class)->finalize(obj);
--- a/libpurple/message.h Thu Aug 27 03:29:49 2020 -0500
+++ b/libpurple/message.h Thu Sep 03 20:16:32 2020 -0500
@@ -1,4 +1,5 @@
* 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
@@ -14,9 +15,8 @@
* 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ * You should have received a copy of the GNU General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. #if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
@@ -25,6 +25,7 @@
@@ -39,6 +40,8 @@
+#include <libpurple/purpleattachment.h> @@ -49,17 +52,56 @@
#define PURPLE_TYPE_MESSAGE purple_message_get_type()
+ * @PURPLE_MESSAGE_SEND: Outgoing message. + * @PURPLE_MESSAGE_RECV: Incoming message. + * @PURPLE_MESSAGE_SYSTEM: System message. + * @PURPLE_MESSAGE_AUTO_RESP: Auto response. + * @PURPLE_MESSAGE_ACTIVE_ONLY: Hint to the UI that this message should not be + * shown in conversations which are only open for + * internal UI purposes (e.g. for contact-aware + * @PURPLE_MESSAGE_NICK: Contains your nick. + * @PURPLE_MESSAGE_NO_LOG: Do not log. + * @PURPLE_MESSAGE_ERROR: Error message. + * @PURPLE_MESSAGE_DELAYED: Delayed message. + * @PURPLE_MESSAGE_RAW: "Raw" message - don't apply formatting + * @PURPLE_MESSAGE_IMAGES: Message contains images + * @PURPLE_MESSAGE_NOTIFY: Message is a notification + * @PURPLE_MESSAGE_NO_LINKIFY: Message should not be auto-linkified + * @PURPLE_MESSAGE_INVISIBLE: Message should not be displayed + * @PURPLE_MESSAGE_REMOTE_SEND: Message sent from another location, + * not an echo of a local one + * Flags applicable to a message. Most will have send, recv or system. +typedef enum /*< flags >*/ + PURPLE_MESSAGE_SEND = 1 << 0, + PURPLE_MESSAGE_RECV = 1 << 1, + PURPLE_MESSAGE_SYSTEM = 1 << 2, + PURPLE_MESSAGE_AUTO_RESP = 1 << 3, + PURPLE_MESSAGE_ACTIVE_ONLY = 1 << 4, + PURPLE_MESSAGE_NICK = 1 << 5, + PURPLE_MESSAGE_NO_LOG = 1 << 6, + PURPLE_MESSAGE_ERROR = 1 << 7, + PURPLE_MESSAGE_DELAYED = 1 << 8, + PURPLE_MESSAGE_RAW = 1 << 9, + PURPLE_MESSAGE_IMAGES = 1 << 10, + PURPLE_MESSAGE_NOTIFY = 1 << 11, + PURPLE_MESSAGE_NO_LINKIFY = 1 << 12, + PURPLE_MESSAGE_INVISIBLE = 1 << 13, + PURPLE_MESSAGE_REMOTE_SEND = 1 << 14, * purple_message_get_type:
* Returns: the #GType for a message.
G_DECLARE_FINAL_TYPE(PurpleMessage, purple_message, PURPLE, MESSAGE, GObject)
-/* conversations.h depends on PurpleMessage and currently PurpleMessageFlag is
-#include <conversation.h>
* purple_message_new_outgoing:
* @who: Message's recipient.
@@ -249,6 +291,59 @@
purple_message_get_flags(PurpleMessage *msg);
+ * purple_message_add_attachment: + * @message: The #PurpleMessage instance. + * @attachment: The #PurpleAttachment instance. + * Adds @attachment to @message. + * Returns %TRUE if an attachment with the same ID did not already exist. +gboolean purple_message_add_attachment(PurpleMessage *message, PurpleAttachment *attachment); + * purple_message_remove_attachment: + * @message: The #PurpleMessage instance. + * @id: The id of the #PurpleAttachment + * Removes the #PurpleAttachment identified by @id if it exists. + * Returns: %TRUE if the #PurpleAttachment was found and removed, %FALSE +gboolean purple_message_remove_attachment(PurpleMessage *message, guint64 id); + * purple_message_get_attachment: + * @message: The #PurpleMessage instance. + * @id: The id of the #PurpleAttachment to get. + * Retrieves the #PurpleAttachment identified by @id from @message. + * Returns: (transfer full): The #PurpleAttachment if it was found, otherwise +PurpleAttachment *purple_message_get_attachment(PurpleMessage *message, guint64 id); + * purple_message_foreach_attachment: + * @message: The #PurpleMessage instance. + * @func: (scope call): The #PurpleAttachmentForeachFunc to call. + * @data: User data to pass to @func. + * Calls @func for each #PurpleAttachment that's attached to @message. +void purple_message_foreach_attachment(PurpleMessage *message, PurpleAttachmentForeachFunc func, gpointer data); + * purple_message_clear_attachments: + * @message: The #PurpleMessage instance. + * Removes all attachments from @message. +void purple_message_clear_attachments(PurpleMessage *message); #endif /* PURPLE_MESSAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleattachment.c Thu Sep 03 20:16:32 2020 -0500
@@ -0,0 +1,318 @@
+ * 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 + * 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 library; if not, see <http://www.gnu.org/licenses/>. +#include "libpurple/purpleattachment.h" +#include <glib/gi18n-lib.h> +struct _PurpleAttachment { +G_DEFINE_TYPE(PurpleAttachment, purple_attachment, G_TYPE_OBJECT); +static GParamSpec *properties[N_PROPERTIES]; +/****************************************************************************** + *****************************************************************************/ +purple_attachment_set_content_type(PurpleAttachment *attachment, + const gchar *content_type) + if(attachment->content_type == content_type) { + g_clear_pointer(&attachment->content_type, g_free); + attachment->content_type = g_strdup(content_type); + g_object_notify_by_pspec(G_OBJECT(attachment), + properties[PROP_CONTENT_TYPE]); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +purple_attachment_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { + PurpleAttachment *attachment = PURPLE_ATTACHMENT(obj); + g_value_set_uint64(value, purple_attachment_get_id(attachment)); + case PROP_CONTENT_TYPE: + g_value_set_string(value, purple_attachment_get_content_type(attachment)); + g_value_set_string(value, purple_attachment_get_local_uri(attachment)); + g_value_set_string(value, purple_attachment_get_remote_uri(attachment)); + g_value_set_uint64(value, purple_attachment_get_size(attachment)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); +purple_attachment_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { + PurpleAttachment *attachment = PURPLE_ATTACHMENT(obj); + purple_attachment_set_id(attachment, g_value_get_uint64(value)); + case PROP_CONTENT_TYPE: + purple_attachment_set_content_type(attachment, g_value_get_string(value)); + purple_attachment_set_local_uri(attachment, g_value_get_string(value)); + purple_attachment_set_remote_uri(attachment, g_value_get_string(value)); + purple_attachment_set_size(attachment, g_value_get_uint64(value)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); +purple_attachment_finalize(GObject *obj) { + PurpleAttachment *attachment = PURPLE_ATTACHMENT(obj); + g_clear_pointer(&attachment->content_type, g_free); + g_clear_pointer(&attachment->local_uri, g_free); + g_clear_pointer(&attachment->remote_uri, g_free); + G_OBJECT_CLASS(purple_attachment_parent_class)->finalize(obj); +purple_attachment_init(PurpleAttachment *attachment) { +purple_attachment_class_init(PurpleAttachmentClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + obj_class->get_property = purple_attachment_get_property; + obj_class->set_property = purple_attachment_set_property; + obj_class->finalize = purple_attachment_finalize; + /* add our properties */ + properties[PROP_ID] = g_param_spec_uint64( + "id", "id", "The identifier of the attachment", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS + properties[PROP_CONTENT_TYPE] = g_param_spec_string( + "content-type", "content-type", "The content type of the attachment", + "application/octet-stream", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS + properties[PROP_LOCAL_URI] = g_param_spec_string( + "local-uri", "local-uri", "The local URI of the attachment", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS + properties[PROP_REMOTE_URI] = g_param_spec_string( + "remote-uri", "remote-uri", "The remote URI of the attachment", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS + properties[PROP_SIZE] = g_param_spec_uint64( + "size", "size", "The file size of the attachment in bytes", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); +/****************************************************************************** + *****************************************************************************/ +purple_attachment_new(guint64 id, const gchar *content_type) { + g_return_val_if_fail(content_type != NULL, NULL); + return PURPLE_ATTACHMENT(g_object_new( + PURPLE_TYPE_ATTACHMENT, + "content-type", content_type, +purple_attachment_get_id(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), 0); +purple_attachment_get_hash_key(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + return &attachment->id; +purple_attachment_set_id(PurpleAttachment *attachment, guint64 id) { + g_return_if_fail(PURPLE_IS_ATTACHMENT(attachment)); + if(attachment->id == id) { + g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_ID]); +purple_attachment_get_content_type(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + return attachment->content_type; +purple_attachment_get_local_uri(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + return attachment->local_uri; +purple_attachment_set_local_uri(PurpleAttachment *attachment, + const gchar *local_uri) + g_return_if_fail(PURPLE_IS_ATTACHMENT(attachment)); + if(attachment->local_uri == local_uri) { + g_free(attachment->local_uri); + if(local_uri != NULL) { + gchar *scheme = g_uri_parse_scheme(local_uri); + attachment->local_uri = g_filename_to_uri(local_uri, NULL, NULL); + attachment->local_uri = g_strdup(local_uri); + attachment->local_uri = NULL; + g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_LOCAL_URI]); +purple_attachment_get_remote_uri(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + return attachment->remote_uri; +purple_attachment_set_remote_uri(PurpleAttachment *attachment, + const gchar *remote_uri) + g_return_if_fail(PURPLE_IS_ATTACHMENT(attachment)); + if(attachment->remote_uri == remote_uri) { + g_free(attachment->remote_uri); + attachment->remote_uri = g_strdup(remote_uri); + g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_REMOTE_URI]); +purple_attachment_get_size(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), 0); + return attachment->size; +purple_attachment_set_size(PurpleAttachment *attachment, guint64 size) { + g_return_if_fail(PURPLE_IS_ATTACHMENT(attachment)); + attachment->size = size; + g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_SIZE]); +purple_attachment_get_filename(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + if(attachment->remote_uri != NULL && attachment->remote_uri[0] != '\0') { + return g_path_get_basename(attachment->remote_uri); + if(attachment->local_uri != NULL && attachment->local_uri[0] != '\0') { + return g_path_get_basename(attachment->local_uri); + return g_strdup("unknown"); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleattachment.h Thu Sep 03 20:16:32 2020 -0500
@@ -0,0 +1,209 @@
+ * 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 + * 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 library; 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" +#ifndef PURPLE_ATTACHMENT_H +#define PURPLE_ATTACHMENT_H + * SECTION:purpleattachment + * @section_id: libpurple-attachment + * @short_description: message attachment + * @title: Message Attachments + * #PurpleAttachment represents a file attached to a #PurpleMessage. +#include <glib-object.h> +#define PURPLE_TYPE_ATTACHMENT purple_attachment_get_type() + * purple_attachment_get_type: + * Returns: the #GType for an attachment. +G_DECLARE_FINAL_TYPE(PurpleAttachment, purple_attachment, PURPLE, ATTACHMENT, GObject) + * PurpleAttachmentForeachFunc: + * @attachment: The #PurpleAttachment instance. + * @data: User supplied data. + * Called when iterating #PurpleAttachment's. +typedef void (*PurpleAttachmentForeachFunc)(PurpleAttachment *attachment, gpointer data); + * purple_attachment_new: + * @id: The identifier of the attachment. + * @content_type: The mime-type of the content. + * Creates a new #PurpleAttachment with the given @id and @content_type. +PurpleAttachment *purple_attachment_new(guint64 id, const gchar *content_type); + * purple_attachment_get_id: + * @attachment: The #PurpleAttachment instance. + * Gets the ID from @attachment. + * Returns: The ID of @attachment. +guint64 purple_attachment_get_id(PurpleAttachment *attachment); + * purple_attachment_get_hash_key: + * @attachment: The #PurpleAttachment instance. + * Gets the hash key of @attachment. This should only be used when + * trying to address a #PurpleAttachment in a #GHashTable that is using + * g_int64_hash() as the key function. + * Returns: (transfer none): The hash key of @attachment. +guint64 *purple_attachment_get_hash_key(PurpleAttachment *attachment); + * purple_attachment_set_id: + * @attachment: The #PurpleAttachment instance. + * @id: The new ID for @attachment. + * Sets the ID of @attachment to @id. +void purple_attachment_set_id(PurpleAttachment *attachment, guint64 id); + * purple_attachment_get_content_type: + * @attachment: The #PurpleAttachment instance. + * Gets the content-type of @attachment. + * Returns: The content-type of @attachment. +const gchar *purple_attachment_get_content_type(PurpleAttachment *attachment); + * purple_attachment_get_local_uri: + * @attachment: The #PurpleAttachment instance. + * Gets the local URI if any for @attachment. + * Returns: (nullable): The local URI for @attachment. +const gchar *purple_attachment_get_local_uri(PurpleAttachment *attachment); + * purple_attachment_set_local_uri: + * @attachment: The #PurpleAttachment instance. + * @local_uri: The new local URI. + * Sets the local URI of @attachment. +void purple_attachment_set_local_uri(PurpleAttachment *attachment, const gchar *local_uri); + * purple_attachment_get_remote_uri: + * @attachment: The #PurpleAttachment instance. + * Gets the remote URI if any for @attachment. + * Returns: (nullable): The remote URI for @attachment. +const gchar *purple_attachment_get_remote_uri(PurpleAttachment *attachment); + * purple_attachment_set_remote_uri: + * @attachment: The #PurpleAttachment instance. + * @remote_uri: The new remote URI. + * Sets the remote URI of @attachment. +void purple_attachment_set_remote_uri(PurpleAttachment *attachment, const gchar *remote_uri); + * purple_attachment_get_size: + * @attachment: The #PurpleAttachment instance. + * Gets the size of @attachment. + * Returns: The size of @attachment. +gsize purple_attachment_get_size(PurpleAttachment *attachment); + * purple_attachment_set_size: + * @attachment: The #PurpleAttachment instance. + * @size: The new size of @attachment. + * Sets the size of @attachment to @size. +void purple_attachment_set_size(PurpleAttachment *attachment, gsize size); + * purple_attachment_get_filename: + * @attachment: The #PurpleAttachment instance. + * Gets the base filename for @attachment. Remote URI will be checked before + * local URI, but the basename of one of those is what will be returned. + * Returns: (transfer full): The filename for @attachment. +gchar *purple_attachment_get_filename(PurpleAttachment *attachment); +#endif /* PURPLE_ATTACHMENT_H */ --- a/pidgin/meson.build Thu Aug 27 03:29:49 2020 -0500
+++ b/pidgin/meson.build Thu Sep 03 20:16:32 2020 -0500
@@ -37,6 +37,7 @@
'pidginaccountchooser.c',
'pidgincontactcompletion.c',
@@ -95,6 +96,7 @@
'pidginaccountchooser.h',
'pidgincontactcompletion.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginattachment.c Thu Sep 03 20:16:32 2020 -0500
@@ -0,0 +1,198 @@
+ * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +#include "pidginattachment.h" +struct _PidginAttachment { + PurpleAttachment *attachment; + PROP_ID = N_PROPERTIES, +static GParamSpec *properties[N_PROPERTIES] = {NULL, }; +/****************************************************************************** + *****************************************************************************/ +pidgin_attachment_set_attachment(PidginAttachment *attachment, + PurpleAttachment *purple_attachment) + if(g_set_object(&attachment->attachment, purple_attachment)) { + g_object_notify_by_pspec(G_OBJECT(attachment), + properties[PROP_ATTACHMENT]); +/****************************************************************************** + * TalkatuAttachment Implementation + *****************************************************************************/ +pidgin_attachment_get_hash_key(TalkatuAttachment *attachment) { + PidginAttachment *wrapper = PIDGIN_ATTACHMENT(attachment); + return purple_attachment_get_hash_key(wrapper->attachment); +pidgin_attachment_talkatu_attachment_init(TalkatuAttachmentInterface *iface) { + iface->get_hash_key = pidgin_attachment_get_hash_key; +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ + TALKATU_TYPE_ATTACHMENT, + pidgin_attachment_talkatu_attachment_init +pidgin_attachment_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { + PidginAttachment *wrapper = PIDGIN_ATTACHMENT(obj); + PurpleAttachment *attachment = wrapper->attachment; + g_value_set_object(value, attachment); + g_value_set_uint(value, purple_attachment_get_id(attachment)); + case PROP_CONTENT_TYPE: + g_value_set_string(value, + purple_attachment_get_content_type(attachment)); + g_value_set_string(value, + purple_attachment_get_local_uri(attachment)); + g_value_set_string(value, + purple_attachment_get_remote_uri(attachment)); + g_value_set_uint64(value, + purple_attachment_get_size(attachment)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +pidgin_attachment_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { + PidginAttachment *attachment = PIDGIN_ATTACHMENT(obj); + pidgin_attachment_set_attachment(attachment, + g_value_get_object(value)); + case PROP_CONTENT_TYPE: + /* we don't allow setting these, if you need to change them, use + * the underlying PurpleAttachment. + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +pidgin_attachment_init(PidginAttachment *attachment) { +pidgin_attachment_finalize(GObject *obj) { + PidginAttachment *attachment = PIDGIN_ATTACHMENT(obj); + g_clear_object(&attachment->attachment); + G_OBJECT_CLASS(pidgin_attachment_parent_class)->finalize(obj); +pidgin_attachment_class_init(PidginAttachmentClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + obj_class->get_property = pidgin_attachment_get_property; + obj_class->set_property = pidgin_attachment_set_property; + obj_class->finalize = pidgin_attachment_finalize; + /* add our custom properties */ + properties[PROP_ATTACHMENT] = g_param_spec_object( + "attachment", "attachment", "The purple attachment", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); + /* add our overridden properties */ + g_object_class_override_property(obj_class, PROP_ID, "id"); + g_object_class_override_property(obj_class, PROP_CONTENT_TYPE, "content-type"); + g_object_class_override_property(obj_class, PROP_LOCAL_URI, "local-uri"); + g_object_class_override_property(obj_class, PROP_REMOTE_URI, "remote-uri"); + g_object_class_override_property(obj_class, PROP_SIZE, "size"); +/****************************************************************************** + *****************************************************************************/ +pidgin_attachment_new(PurpleAttachment *attachment) { + PIDGIN_TYPE_ATTACHMENT, + "attachment", attachment, +pidgin_attachment_get_attachment(PidginAttachment *attachment) { + g_return_val_if_fail(PIDGIN_IS_ATTACHMENT(attachment), NULL); + return attachment->attachment; --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginattachment.h Thu Sep 03 20:16:32 2020 -0500
@@ -0,0 +1,67 @@
+ * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. +#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) +# error "only <pidgin.h> may be included directly" +#ifndef PIDGIN_ATTACHMENT_H +#define PIDGIN_ATTACHMENT_H + * SECTION:pidginattachment + * @section_id: pidgin-pidginattachment + * @short_description: <filename>pidginattachment.h</filename> + * @title: Pidgin Attachment API +#define PIDGIN_TYPE_ATTACHMENT (pidgin_attachment_get_type()) +G_DECLARE_FINAL_TYPE(PidginAttachment, pidgin_attachment, PIDGIN, ATTACHMENT, GObject) + * pidgin_attachment_new: + * @attachment: The #PurpleAttachment to wrap. + * Wraps @attachment so that it can be used as a #TalkatuAttachment. + * Returns: (transfer full): The new #PidginAttachment instance. +PidginAttachment *pidgin_attachment_new(PurpleAttachment *attachment); + * pidgin_attachment_get_attachment: + * @attachment: The #PidginAttachment instance. + * Gets the #PurpleAttachment that @attachment is wrapping. + * Returns: (transfer none): The #PurpleAttachment that @attachment is wrapping. +PurpleAttachment *pidgin_attachment_get_attachment(PidginAttachment *attachment); +#endif /* PIDGIN_ATTACHMENT_H */ --- a/pidgin/pidginmessage.c Thu Aug 27 03:29:49 2020 -0500
+++ b/pidgin/pidginmessage.c Thu Sep 03 20:16:32 2020 -0500
@@ -21,13 +21,20 @@
#include "pidginmessage.h"
+#include "pidginattachment.h"
+ PurpleMessage *message; + TalkatuAttachmentForeachFunc func; +} PidginMessageAttachmentForeachData; @@ -46,24 +53,118 @@
*****************************************************************************/
-pidgin_message_set_message(PidginMessage *msg, PurpleMessage *purple_msg) {
- if(g_set_object(&msg->msg, purple_msg)) {
- g_clear_pointer(&msg->timestamp, g_date_time_unref);
- msg->timestamp = g_date_time_new_from_unix_local(purple_message_get_time(purple_msg));
+pidgin_message_set_message(PidginMessage *message, PurpleMessage *purple_msg) { + if(g_set_object(&message->message, purple_msg)) { + g_clear_pointer(&message->timestamp, g_date_time_unref); + message->timestamp = g_date_time_new_from_unix_local(purple_message_get_time(purple_msg)); - g_object_freeze_notify(G_OBJECT(msg));
- g_object_notify_by_pspec(G_OBJECT(msg), properties[PROP_MESSAGE]);
- g_object_notify(G_OBJECT(msg), "timestamp");
- g_object_thaw_notify(G_OBJECT(msg));
+ g_object_freeze_notify(G_OBJECT(message)); + g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_MESSAGE]); + g_object_notify(G_OBJECT(message), "timestamp"); + g_object_thaw_notify(G_OBJECT(message)); /******************************************************************************
* TalkatuMessage Implementation
*****************************************************************************/
+pidgin_message_add_attachment(TalkatuMessage *tmessage, + TalkatuAttachment *tattachment) + PidginMessage *pmessage = PIDGIN_MESSAGE(tmessage); + PurpleAttachment *pattachment = NULL; + pattachment = purple_attachment_new( + talkatu_attachment_get_id(tattachment), + talkatu_attachment_get_content_type(tattachment) + ret = purple_message_add_attachment(pmessage->message, pattachment); + g_object_unref(G_OBJECT(pattachment)); +pidgin_message_remove_attachment(TalkatuMessage *tmessage, guint64 id) { + PidginMessage *pmessage = PIDGIN_MESSAGE(tmessage); + return purple_message_remove_attachment(pmessage->message, id); +static TalkatuAttachment * +pidgin_message_get_attachment(TalkatuMessage *tmessage, guint64 id) { + PidginMessage *pmessage = PIDGIN_MESSAGE(tmessage); + PidginAttachment *pidgin_attachment = NULL; + PurpleAttachment *purple_attachment = NULL; + purple_attachment = purple_message_get_attachment(pmessage->message, id); + pidgin_attachment = pidgin_attachment_new(purple_attachment); + g_object_unref(G_OBJECT(purple_attachment)); + return TALKATU_ATTACHMENT(pidgin_attachment); +pidgin_message_foreach_attachment_helper(PurpleAttachment *attachment, + PidginAttachment *pidgin_attachment = NULL; + PidginMessageAttachmentForeachData *d = NULL; + d = (PidginMessageAttachmentForeachData *)data; + pidgin_attachment = pidgin_attachment_new(attachment); + d->func(TALKATU_ATTACHMENT(pidgin_attachment), d->data); + g_object_unref(G_OBJECT(pidgin_attachment)); +pidgin_message_foreach_attachment(TalkatuMessage *tmessage, + TalkatuAttachmentForeachFunc func, + PidginMessage *pmessage = PIDGIN_MESSAGE(tmessage); + PidginMessageAttachmentForeachData *d = NULL; + /* PurpleAttachmentForeachFunc and TalkatuAttachmentForeachFunc may not + * always have the same signature. So to work around that, we use a helper + * function that has the signature of PurpleAttachmentForeachFunc but will + * call the TalkatuAttachmentForeachFunc while also wrapping the + d = g_new(PidginMessageAttachmentForeachData, 1); + purple_message_foreach_attachment( + pidgin_message_foreach_attachment_helper, +pidgin_message_clear_attachments(TalkatuMessage *tmessage) { + PidginMessage *pmessage = PIDGIN_MESSAGE(tmessage); + purple_message_clear_attachments(pmessage->message); pidgin_message_talkatu_message_init(TalkatuMessageInterface *iface) {
- /* we don't actually change behavior, we just override properties */
+ iface->add_attachment = pidgin_message_add_attachment; + iface->remove_attachment = pidgin_message_remove_attachment; + iface->get_attachment = pidgin_message_get_attachment; + iface->foreach_attachment = pidgin_message_foreach_attachment; + iface->clear_attachments = pidgin_message_clear_attachments; /******************************************************************************
@@ -79,26 +180,26 @@
pidgin_message_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) {
- PidginMessage *msg = PIDGIN_MESSAGE(obj);
+ PidginMessage *message = PIDGIN_MESSAGE(obj); - g_value_set_object(value, msg->msg);
+ g_value_set_object(value, message->message); - g_value_set_uint(value, purple_message_get_id(msg->msg));
+ g_value_set_uint(value, purple_message_get_id(message->message)); g_value_set_enum(value, TALKATU_CONTENT_TYPE_PLAIN);
- g_value_set_string(value, purple_message_get_author(msg->msg));
+ g_value_set_string(value, purple_message_get_author(message->message)); - g_value_set_string(value, purple_message_get_contents(msg->msg));
+ g_value_set_string(value, purple_message_get_contents(message->message)); - g_value_set_pointer(value, msg->timestamp);
+ g_value_set_pointer(value, message->timestamp); g_value_set_boolean(value, FALSE);
@@ -111,11 +212,11 @@
pidgin_message_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) {
- PidginMessage *msg = PIDGIN_MESSAGE(obj);
+ PidginMessage *message = PIDGIN_MESSAGE(obj); - pidgin_message_set_message(msg, g_value_get_object(value));
+ pidgin_message_set_message(message, g_value_get_object(value)); @@ -132,15 +233,15 @@
-pidgin_message_init(PidginMessage *msg) {
+pidgin_message_init(PidginMessage *message) { pidgin_message_finalize(GObject *obj) {
- PidginMessage *msg = PIDGIN_MESSAGE(obj);
+ PidginMessage *message = PIDGIN_MESSAGE(obj); - g_clear_object(&msg->msg);
- g_clear_pointer(&msg->timestamp, g_date_time_unref);
+ g_clear_object(&message->message); + g_clear_pointer(&message->timestamp, g_date_time_unref); G_OBJECT_CLASS(pidgin_message_parent_class)->finalize(obj);
@@ -175,17 +276,17 @@
*****************************************************************************/
-pidgin_message_new(PurpleMessage *msg) {
+pidgin_message_new(PurpleMessage *message) {
-pidgin_message_get_message(PidginMessage *msg) {
- g_return_val_if_fail(PIDGIN_IS_MESSAGE(msg), NULL);
+pidgin_message_get_message(PidginMessage *message) { + g_return_val_if_fail(PIDGIN_IS_MESSAGE(message), NULL);
+ return message->message; --- a/pidgin/pidginmessage.h Thu Aug 27 03:29:49 2020 -0500
+++ b/pidgin/pidginmessage.h Thu Sep 03 20:16:32 2020 -0500
@@ -1,4 +1,5 @@
* Pidgin is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
@@ -15,8 +16,7 @@
* 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ * along with this program; if not, see <https://www.gnu.org/licenses/>. #if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
--- a/po/POTFILES.in Thu Aug 27 03:29:49 2020 -0500
+++ b/po/POTFILES.in Thu Sep 03 20:16:32 2020 -0500
@@ -266,6 +266,7 @@
libpurple/protocols/zephyr/ZWait4Not.c
+libpurple/purpleattachment.c libpurple/purplechatuser.c
libpurple/purpleimconversation.c
libpurple/purpleprotocolim.c
@@ -343,6 +344,7 @@
+pidgin/pidginattachment.c pidgin/pidgincontactcompletion.c