Thu, 03 Sep 2020 20:16:32 -0500
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 @@ <title>Messaging</title> <xi:include href="xml/message.xml" /> + <xi:include href="xml/purpleattachment.xml" /> <xi:include href="xml/log.xml" /> <xi:include href="xml/cmds.xml" /> </chapter>
--- 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; -/** - * PurpleMessageFlags: - * @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 - * conversations). - * @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 - * Since: 2.12.0 - * - * 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, -} PurpleMessageFlags; - #include <glib.h> #include <glib-object.h> #include "message.h"
--- 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', 'purplechatuser.c', 'purpleimconversation.c', + 'purpleattachment.c', 'purplekeyvaluepair.c', 'purpleprotocolfactory.c', 'purpleprotocolim.c', @@ -133,6 +134,7 @@ 'purpleaccountusersplit.h', 'purplechatuser.h', 'purpleimconversation.h', + 'purpleattachment.h', 'purplekeyvaluepair.h', 'purpleprotocolfactory.h', 'purpleprotocolim.h', @@ -206,6 +208,7 @@ 'conversation.h', 'debug.h', 'eventloop.h', + 'message.h', 'notify.h', 'plugins.h', 'protocol.h',
--- 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 @@ gchar *contents; guint64 msgtime; PurpleMessageFlags flags; + + GHashTable *attachments; } PurpleMessagePrivate; enum @@ -246,6 +248,80 @@ return priv->flags; } +gboolean +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))); +} + +gboolean +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); +} + +PurpleAttachment * +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))); + } + + return NULL; +} + +void +purple_message_foreach_attachment(PurpleMessage *message, + PurpleAttachmentForeachFunc func, + gpointer data) +{ + PurpleMessagePrivate *priv = NULL; + GHashTableIter iter; + gpointer value; + + 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); + } +} + +void +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); +} + /****************************************************************************** * Object stuff ******************************************************************************/ @@ -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, + NULL, g_object_unref); + priv->id = ++max_id; g_hash_table_insert(messages, GINT_TO_POINTER(max_id), msg); } @@ -272,6 +351,8 @@ g_free(priv->recipient); g_free(priv->contents); + 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 +/* + * 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 @@ -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 @@ #ifndef PURPLE_MESSAGE_H #define PURPLE_MESSAGE_H + /** * SECTION:message * @include:message.h @@ -39,6 +40,8 @@ #include <glib-object.h> +#include <libpurple/purpleattachment.h> + G_BEGIN_DECLS /** @@ -49,17 +52,56 @@ #define PURPLE_TYPE_MESSAGE purple_message_get_type() /** + * PurpleMessageFlags: + * @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 + * conversations). + * @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 + * Since: 2.12.0 + * + * 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, +} PurpleMessageFlags; + +/** * 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 - * in conversations.h. - */ -#include <conversation.h> - /** * purple_message_new_outgoing: * @who: Message's recipient. @@ -249,6 +291,59 @@ PurpleMessageFlags 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 + * otherwise. + */ +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 + * %NULL. + */ +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); + G_END_DECLS #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 + * + * 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 library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "libpurple/purpleattachment.h" + +#include <glib/gi18n-lib.h> + +struct _PurpleAttachment { + GObject parent; + + guint64 id; + gchar *content_type; + + gchar *local_uri; + gchar *remote_uri; + + guint64 size; +}; + +G_DEFINE_TYPE(PurpleAttachment, purple_attachment, G_TYPE_OBJECT); + +enum { + PROP_0 = 0, + PROP_ID, + PROP_CONTENT_TYPE, + PROP_LOCAL_URI, + PROP_REMOTE_URI, + PROP_SIZE, + N_PROPERTIES, +}; +static GParamSpec *properties[N_PROPERTIES]; + +/****************************************************************************** + * Private Setters + *****************************************************************************/ +static void +purple_attachment_set_content_type(PurpleAttachment *attachment, + const gchar *content_type) +{ + if(attachment->content_type == content_type) { + return; + } + + 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 + *****************************************************************************/ +static void +purple_attachment_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { + PurpleAttachment *attachment = PURPLE_ATTACHMENT(obj); + + switch(prop_id) { + case PROP_ID: + g_value_set_uint64(value, purple_attachment_get_id(attachment)); + break; + case PROP_CONTENT_TYPE: + g_value_set_string(value, purple_attachment_get_content_type(attachment)); + break; + case PROP_LOCAL_URI: + g_value_set_string(value, purple_attachment_get_local_uri(attachment)); + break; + case PROP_REMOTE_URI: + g_value_set_string(value, purple_attachment_get_remote_uri(attachment)); + break; + case PROP_SIZE: + g_value_set_uint64(value, purple_attachment_get_size(attachment)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +purple_attachment_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { + PurpleAttachment *attachment = PURPLE_ATTACHMENT(obj); + + switch(prop_id) { + case PROP_ID: + purple_attachment_set_id(attachment, g_value_get_uint64(value)); + break; + case PROP_CONTENT_TYPE: + purple_attachment_set_content_type(attachment, g_value_get_string(value)); + break; + case PROP_LOCAL_URI: + purple_attachment_set_local_uri(attachment, g_value_get_string(value)); + break; + case PROP_REMOTE_URI: + purple_attachment_set_remote_uri(attachment, g_value_get_string(value)); + break; + case PROP_SIZE: + purple_attachment_set_size(attachment, g_value_get_uint64(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +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); +} + +static void +purple_attachment_init(PurpleAttachment *attachment) { +} + +static void +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", + 0, G_MAXUINT64, 0, + 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", + NULL, + 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", + NULL, + 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", + 0, G_MAXUINT64, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS + ); + + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); +} + +/****************************************************************************** + * Public API + *****************************************************************************/ + +PurpleAttachment * +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, + "id", id, + "content-type", content_type, + NULL + )); +} + +guint64 +purple_attachment_get_id(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), 0); + + return attachment->id; +} + +guint64 * +purple_attachment_get_hash_key(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + + return &attachment->id; +} + +void +purple_attachment_set_id(PurpleAttachment *attachment, guint64 id) { + g_return_if_fail(PURPLE_IS_ATTACHMENT(attachment)); + + if(attachment->id == id) { + return; + } + + attachment->id = id; + + g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_ID]); +} + +const gchar * +purple_attachment_get_content_type(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + + return attachment->content_type; +} + +const gchar * +purple_attachment_get_local_uri(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + + return attachment->local_uri; +} + +void +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) { + return; + } + + g_free(attachment->local_uri); + + if(local_uri != NULL) { + gchar *scheme = g_uri_parse_scheme(local_uri); + if(scheme == NULL) { + attachment->local_uri = g_filename_to_uri(local_uri, NULL, NULL); + } else { + g_free(scheme); + attachment->local_uri = g_strdup(local_uri); + } + } else { + attachment->local_uri = NULL; + } + + g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_LOCAL_URI]); +} + +const gchar * +purple_attachment_get_remote_uri(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL); + + return attachment->remote_uri; +} + +void +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) { + return; + } + + g_free(attachment->remote_uri); + attachment->remote_uri = g_strdup(remote_uri); + + g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_REMOTE_URI]); +} + +guint64 +purple_attachment_get_size(PurpleAttachment *attachment) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), 0); + + return attachment->size; +} + +void +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]); +} + +gchar * +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 + * + * 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 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" +#endif + +#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> + +G_BEGIN_DECLS + +#define PURPLE_TYPE_ATTACHMENT purple_attachment_get_type() + +/** + * purple_attachment_get_type: + * + * Returns: the #GType for an attachment. + * + * Since: 3.0.0 + */ +G_DECLARE_FINAL_TYPE(PurpleAttachment, purple_attachment, PURPLE, ATTACHMENT, GObject) + +/** + * PurpleAttachmentForeachFunc: + * @attachment: The #PurpleAttachment instance. + * @data: User supplied data. + * + * Called when iterating #PurpleAttachment's. + * + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +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. + * + * Since: 3.0.0 + */ +gchar *purple_attachment_get_filename(PurpleAttachment *attachment); + +G_END_DECLS + +#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', 'pidginaccountsmenu.c', 'pidginactiongroup.c', + 'pidginattachment.c', 'pidginbuddylistmenu.c', 'pidgincontactcompletion.c', 'pidgindebug.c', @@ -95,6 +96,7 @@ 'pidginaccountchooser.h', 'pidginaccountsmenu.h', 'pidginactiongroup.h', + 'pidginattachment.h', 'pidginbuddylistmenu.h', 'pidgincontactcompletion.h', 'pidgincore.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 + * + * 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "pidginattachment.h" + +struct _PidginAttachment { + GObject parent; + + PurpleAttachment *attachment; +}; + +enum { + PROP_0, + PROP_ATTACHMENT, + N_PROPERTIES, + /* overrides */ + PROP_ID = N_PROPERTIES, + PROP_CONTENT_TYPE, + PROP_LOCAL_URI, + PROP_REMOTE_URI, + PROP_SIZE, +}; +static GParamSpec *properties[N_PROPERTIES] = {NULL, }; + +/****************************************************************************** + * Helpers + *****************************************************************************/ +static void +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 + *****************************************************************************/ +static guint64 * +pidgin_attachment_get_hash_key(TalkatuAttachment *attachment) { + PidginAttachment *wrapper = PIDGIN_ATTACHMENT(attachment); + + return purple_attachment_get_hash_key(wrapper->attachment); +} + +static void +pidgin_attachment_talkatu_attachment_init(TalkatuAttachmentInterface *iface) { + iface->get_hash_key = pidgin_attachment_get_hash_key; +} + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +G_DEFINE_TYPE_EXTENDED( + PidginAttachment, + pidgin_attachment, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE( + TALKATU_TYPE_ATTACHMENT, + pidgin_attachment_talkatu_attachment_init + ) +); + +static void +pidgin_attachment_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { + PidginAttachment *wrapper = PIDGIN_ATTACHMENT(obj); + PurpleAttachment *attachment = wrapper->attachment; + + switch(param_id) { + case PROP_ATTACHMENT: + g_value_set_object(value, attachment); + break; + case PROP_ID: + g_value_set_uint(value, purple_attachment_get_id(attachment)); + break; + case PROP_CONTENT_TYPE: + g_value_set_string(value, + purple_attachment_get_content_type(attachment)); + break; + case PROP_LOCAL_URI: + g_value_set_string(value, + purple_attachment_get_local_uri(attachment)); + break; + case PROP_REMOTE_URI: + g_value_set_string(value, + purple_attachment_get_remote_uri(attachment)); + break; + case PROP_SIZE: + g_value_set_uint64(value, + purple_attachment_get_size(attachment)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +static void +pidgin_attachment_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { + PidginAttachment *attachment = PIDGIN_ATTACHMENT(obj); + + switch(param_id) { + case PROP_ATTACHMENT: + pidgin_attachment_set_attachment(attachment, + g_value_get_object(value)); + break; + case PROP_ID: + case PROP_CONTENT_TYPE: + case PROP_LOCAL_URI: + case PROP_REMOTE_URI: + case PROP_SIZE: + /* we don't allow setting these, if you need to change them, use + * the underlying PurpleAttachment. + */ + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +static void +pidgin_attachment_init(PidginAttachment *attachment) { +} + +static void +pidgin_attachment_finalize(GObject *obj) { + PidginAttachment *attachment = PIDGIN_ATTACHMENT(obj); + + g_clear_object(&attachment->attachment); + + G_OBJECT_CLASS(pidgin_attachment_parent_class)->finalize(obj); +} + +static void +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", + PURPLE_TYPE_MESSAGE, + 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"); +} + +/****************************************************************************** + * API + *****************************************************************************/ +PidginAttachment * +pidgin_attachment_new(PurpleAttachment *attachment) { + return g_object_new( + PIDGIN_TYPE_ATTACHMENT, + "attachment", attachment, + NULL + ); +} + +PurpleAttachment * +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 + * + * 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 + * 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(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) +# error "only <pidgin.h> may be included directly" +#endif + +#ifndef PIDGIN_ATTACHMENT_H +#define PIDGIN_ATTACHMENT_H + +/** + * SECTION:pidginattachment + * @section_id: pidgin-pidginattachment + * @short_description: <filename>pidginattachment.h</filename> + * @title: Pidgin Attachment API + */ + +#include <purple.h> + +#include <talkatu.h> + +G_BEGIN_DECLS + +#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); + +G_END_DECLS + +#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" + struct _PidginMessage { GObject parent; - PurpleMessage *msg; + PurpleMessage *message; GDateTime *timestamp; }; +typedef struct { + TalkatuAttachmentForeachFunc func; + gpointer data; +} PidginMessageAttachmentForeachData; + enum { PROP_0, PROP_MESSAGE, @@ -46,24 +53,118 @@ * Helpers *****************************************************************************/ static void -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 *****************************************************************************/ +static gboolean +pidgin_message_add_attachment(TalkatuMessage *tmessage, + TalkatuAttachment *tattachment) +{ + PidginMessage *pmessage = PIDGIN_MESSAGE(tmessage); + PurpleAttachment *pattachment = NULL; + gboolean ret = FALSE; + + 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)); + + return ret; +} + +static gboolean +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); +} + +static void +pidgin_message_foreach_attachment_helper(PurpleAttachment *attachment, + gpointer data) +{ + 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)); +} + +static void +pidgin_message_foreach_attachment(TalkatuMessage *tmessage, + TalkatuAttachmentForeachFunc func, + gpointer data) +{ + 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 + * PurpleAttachments. + */ + + d = g_new(PidginMessageAttachmentForeachData, 1); + d->func = func; + d->data = data; + + purple_message_foreach_attachment( + pmessage->message, + pidgin_message_foreach_attachment_helper, + d + ); + + g_free(d); +} + +static void +pidgin_message_clear_attachments(TalkatuMessage *tmessage) { + PidginMessage *pmessage = PIDGIN_MESSAGE(tmessage); + + purple_message_clear_attachments(pmessage->message); +} + static void 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 @@ static void pidgin_message_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { - PidginMessage *msg = PIDGIN_MESSAGE(obj); + PidginMessage *message = PIDGIN_MESSAGE(obj); switch(param_id) { case PROP_MESSAGE: - g_value_set_object(value, msg->msg); + g_value_set_object(value, message->message); break; case PROP_ID: - g_value_set_uint(value, purple_message_get_id(msg->msg)); + g_value_set_uint(value, purple_message_get_id(message->message)); break; case PROP_CONTENT_TYPE: g_value_set_enum(value, TALKATU_CONTENT_TYPE_PLAIN); break; case PROP_AUTHOR: - g_value_set_string(value, purple_message_get_author(msg->msg)); + g_value_set_string(value, purple_message_get_author(message->message)); break; case PROP_CONTENTS: - g_value_set_string(value, purple_message_get_contents(msg->msg)); + g_value_set_string(value, purple_message_get_contents(message->message)); break; case PROP_TIMESTAMP: - g_value_set_pointer(value, msg->timestamp); + g_value_set_pointer(value, message->timestamp); break; case PROP_EDITED: g_value_set_boolean(value, FALSE); @@ -111,11 +212,11 @@ static void pidgin_message_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { - PidginMessage *msg = PIDGIN_MESSAGE(obj); + PidginMessage *message = PIDGIN_MESSAGE(obj); switch(param_id) { case PROP_MESSAGE: - pidgin_message_set_message(msg, g_value_get_object(value)); + pidgin_message_set_message(message, g_value_get_object(value)); break; case PROP_ID: case PROP_CONTENT_TYPE: @@ -132,15 +233,15 @@ } static void -pidgin_message_init(PidginMessage *msg) { +pidgin_message_init(PidginMessage *message) { } static void 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 @@ * API *****************************************************************************/ PidginMessage * -pidgin_message_new(PurpleMessage *msg) { +pidgin_message_new(PurpleMessage *message) { return g_object_new( PIDGIN_TYPE_MESSAGE, - "message", msg, + "message", message, NULL ); } PurpleMessage * -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 msg->msg; + 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 +/* + * pidgin * * 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/proxy.c libpurple/purple-gio.c +libpurple/purpleattachment.c libpurple/purplechatuser.c libpurple/purpleimconversation.c libpurple/purpleprotocolim.c @@ -343,6 +344,7 @@ pidgin/minidialog.c pidgin/pidginabout.c pidgin/pidgin.c +pidgin/pidginattachment.c pidgin/pidgincontactcompletion.c pidgin/pidgindebug.c pidgin/pidgingdkpixbuf.c