# HG changeset patch
# Node ID 93996e9119a26c90dc292e346f74ce7e86757250
# Parent  74178b555072104fd299668087c29228feeffbfd
diff --git a/libpurple/purpleattachment.c b/libpurple/purpleattachment.c
--- a/libpurple/purpleattachment.c
+++ b/libpurple/purpleattachment.c
@@ -29,6 +29,7 @@
 
 	guint64 id;
 	char *content_type;
+	gboolean _inline;
 
 	char *local_uri;
 	char *remote_uri;
@@ -37,9 +38,10 @@
 };
 
 enum {
-	PROP_0 = 0,
+	PROP_0,
 	PROP_ID,
 	PROP_CONTENT_TYPE,
+	PROP_INLINE,
 	PROP_LOCAL_URI,
 	PROP_REMOTE_URI,
 	PROP_SIZE,
@@ -48,19 +50,6 @@
 static GParamSpec *properties[N_PROPERTIES] = {NULL, };
 
 /******************************************************************************
- * Private Setters
- *****************************************************************************/
-static void
-purple_attachment_set_content_type(PurpleAttachment *attachment,
-                                   const char *content_type)
-{
-	if(g_set_str(&attachment->content_type, content_type)) {
-		g_object_notify_by_pspec(G_OBJECT(attachment),
-		                         properties[PROP_CONTENT_TYPE]);
-	}
-}
-
-/******************************************************************************
  * GObject Implementation
  *****************************************************************************/
 G_DEFINE_FINAL_TYPE(PurpleAttachment, purple_attachment, G_TYPE_OBJECT);
@@ -79,6 +68,9 @@
 		g_value_set_string(value,
 		                   purple_attachment_get_content_type(attachment));
 		break;
+	case PROP_INLINE:
+		g_value_set_boolean(value, purple_attachment_get_inline(attachment));
+		break;
 	case PROP_LOCAL_URI:
 		g_value_set_string(value, purple_attachment_get_local_uri(attachment));
 		break;
@@ -109,6 +101,9 @@
 		purple_attachment_set_content_type(attachment,
 		                                   g_value_get_string(value));
 		break;
+	case PROP_INLINE:
+		purple_attachment_set_inline(attachment, g_value_get_boolean(value));
+		break;
 	case PROP_LOCAL_URI:
 		purple_attachment_set_local_uri(attachment, g_value_get_string(value));
 		break;
@@ -171,7 +166,23 @@
 	properties[PROP_CONTENT_TYPE] = g_param_spec_string(
 		"content-type", NULL, NULL,
 		"application/octet-stream",
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleAttachment:inline:
+	 *
+	 * Whether or not the attachment should be shown inline.
+	 *
+	 * This property is a hint that user interfaces may use to only show this
+	 * attachment inline with message content instead of as an external
+	 * attachment.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_INLINE] = g_param_spec_boolean(
+		"inline", NULL, NULL,
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleAttachment:local-uri:
@@ -228,6 +239,16 @@
 		NULL);
 }
 
+gboolean
+purple_attachment_equal(PurpleAttachment *attachment1,
+                        PurpleAttachment *attachment2)
+{
+	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment1), FALSE);
+	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment2), FALSE);
+
+	return (attachment1->id == attachment2->id);
+}
+
 guint64
 purple_attachment_get_id(PurpleAttachment *attachment) {
 	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), 0);
@@ -255,6 +276,25 @@
 	g_object_notify_by_pspec(G_OBJECT(attachment), properties[PROP_ID]);
 }
 
+gboolean
+purple_attachment_get_inline(PurpleAttachment *attachment) {
+	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), FALSE);
+
+	return attachment->_inline;
+}
+
+void
+purple_attachment_set_inline(PurpleAttachment *attachment, gboolean _inline) {
+	g_return_if_fail(PURPLE_IS_ATTACHMENT(attachment));
+
+	if(attachment->_inline != _inline) {
+		attachment->_inline = _inline;
+
+		g_object_notify_by_pspec(G_OBJECT(attachment),
+		                         properties[PROP_INLINE]);
+	}
+}
+
 const char *
 purple_attachment_get_content_type(PurpleAttachment *attachment) {
 	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL);
@@ -262,6 +302,16 @@
 	return attachment->content_type;
 }
 
+void
+purple_attachment_set_content_type(PurpleAttachment *attachment,
+                                   const char *content_type)
+{
+	if(g_set_str(&attachment->content_type, content_type)) {
+		g_object_notify_by_pspec(G_OBJECT(attachment),
+		                         properties[PROP_CONTENT_TYPE]);
+	}
+}
+
 const char *
 purple_attachment_get_local_uri(PurpleAttachment *attachment) {
 	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), NULL);
@@ -275,25 +325,10 @@
 {
 	g_return_if_fail(PURPLE_IS_ATTACHMENT(attachment));
 
-	if(attachment->local_uri == local_uri) {
-		return;
+	if(g_set_str(&attachment->local_uri, local_uri)) {
+		g_object_notify_by_pspec(G_OBJECT(attachment),
+		                         properties[PROP_LOCAL_URI]);
 	}
-
-	g_free(attachment->local_uri);
-
-	if(local_uri != NULL) {
-		char *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 char *
diff --git a/libpurple/purpleattachment.h b/libpurple/purpleattachment.h
--- a/libpurple/purpleattachment.h
+++ b/libpurple/purpleattachment.h
@@ -72,6 +72,22 @@
 PurpleAttachment *purple_attachment_new(guint64 id, const char *content_type);
 
 /**
+ * purple_attachment_equal:
+ * @attachment2: the second instance
+ *
+ * Checks if two attachments are equal.
+ *
+ * Attachments are considered equal if their [property@Attachment:id]
+ * properties are equal.
+ *
+ * Returns: true if the attachments are equal; otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_attachment_equal(PurpleAttachment *attachment1, PurpleAttachment *attachment2);
+
+/**
  * purple_attachment_get_id:
  * @attachment: The #PurpleAttachment instance.
  *
@@ -112,6 +128,29 @@
 void purple_attachment_set_id(PurpleAttachment *attachment, guint64 id);
 
 /**
+ * purple_attachment_get_inline:
+ *
+ * Gets whether or not the attach should be displayed inline.
+ *
+ * Returns: true if the attachment should be displayed inline; otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_attachment_get_inline(PurpleAttachment *attachment);
+
+/**
+ * purple_attachment_set_inline:
+ * @_inline: the new value
+ *
+ * Sets whether or not the attachment should be displayed inline.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_attachment_set_inline(PurpleAttachment *attachment, gboolean _inline);
+
+/**
  * purple_attachment_get_content_type:
  * @attachment: The #PurpleAttachment instance.
  *
@@ -125,6 +164,17 @@
 const char *purple_attachment_get_content_type(PurpleAttachment *attachment);
 
 /**
+ * purple_attachment_set_content_type:
+ * @content_type: (nullable): the new content-type
+ *
+ * Sets the content type of the attachment.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_attachment_set_content_type(PurpleAttachment *attachment, const char *content_type);
+
+/**
  * purple_attachment_get_local_uri:
  * @attachment: The #PurpleAttachment instance.
  *
diff --git a/libpurple/tests/meson.build b/libpurple/tests/meson.build
--- a/libpurple/tests/meson.build
+++ b/libpurple/tests/meson.build
@@ -2,6 +2,7 @@
     'account',
     'account_option',
     'account_manager',
+    'attachment',
     'authorization_request',
     'badge',
     'badge_manager',
diff --git a/libpurple/tests/test_attachment.c b/libpurple/tests/test_attachment.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/test_attachment.c
@@ -0,0 +1,193 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <purple.h>
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+test_purple_attachment_notify_counter_cb(GObject *self,
+                                         G_GNUC_UNUSED GParamSpec *pspec,
+                                         gpointer data)
+{
+	guint *counter = data;
+
+	g_assert_true(PURPLE_IS_ATTACHMENT(self));
+
+	*counter = *counter + 1;
+}
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_purple_attachment_new(void) {
+	PurpleAttachment *attachment = NULL;
+
+	attachment = purple_attachment_new(1337lu, "text/plain");
+
+	g_assert_true(PURPLE_IS_ATTACHMENT(attachment));
+
+	g_assert_finalize_object(attachment);
+}
+
+static void
+test_purple_attachment_properties(void) {
+	PurpleAttachment *attachment = NULL;
+	char *content_type = NULL;
+	char *local_uri = NULL;
+	char *remote_uri = NULL;
+	gboolean _inline = FALSE;
+	guint64 in_size = 0x1337;
+	guint64 out_size = 0;
+
+	attachment = g_object_new(
+		PURPLE_TYPE_ATTACHMENT,
+		"content-type", "text/plain",
+		"inline", TRUE,
+		"local-uri", "local-uri",
+		"remote-uri", "remote-uri",
+		"size", in_size,
+		NULL);
+
+	g_object_get(
+		attachment,
+		"content-type", &content_type,
+		"inline", &_inline,
+		"local-uri", &local_uri,
+		"remote-uri", &remote_uri,
+		"size", &out_size,
+		NULL);
+
+	g_assert_cmpstr(content_type, ==, "text/plain");
+	g_clear_pointer(&content_type, g_free);
+
+	g_assert_true(_inline);
+
+	g_assert_cmpstr(local_uri, ==, "local-uri");
+	g_clear_pointer(&local_uri, g_free);
+
+	g_assert_cmpstr(remote_uri, ==, "remote-uri");
+	g_clear_pointer(&remote_uri, g_free);
+
+	g_assert_cmpuint(out_size, ==, in_size);
+
+	g_assert_finalize_object(attachment);
+}
+
+static void
+test_purple_attachment_notify(void) {
+	PurpleAttachment *attachment = NULL;
+	guint counter = 0;
+
+	attachment = g_object_new(PURPLE_TYPE_ATTACHMENT, NULL);
+	g_signal_connect(attachment, "notify",
+	                 G_CALLBACK(test_purple_attachment_notify_counter_cb),
+	                 &counter);
+
+	counter = 0;
+	purple_attachment_set_content_type(attachment, "text/plain");
+	g_assert_cmpuint(counter, ==, 1);
+
+	counter = 0;
+	purple_attachment_set_id(attachment, 1337lu);
+	g_assert_cmpuint(counter, ==, 1);
+
+	counter = 0;
+	purple_attachment_set_inline(attachment, TRUE);
+	g_assert_cmpuint(counter, ==, 1);
+
+	counter = 0;
+	purple_attachment_set_local_uri(attachment, "/dev/null");
+	g_assert_cmpuint(counter, ==, 1);
+
+	counter = 0;
+	purple_attachment_set_remote_uri(attachment, "/dev/null");
+	g_assert_cmpuint(counter, ==, 1);
+
+	counter = 0;
+	purple_attachment_set_size(attachment, 42lu);
+	g_assert_cmpuint(counter, ==, 1);
+
+	g_assert_finalize_object(attachment);
+}
+
+static void
+test_purple_attachment_equal_self(void) {
+	PurpleAttachment *attachment = NULL;
+
+	attachment = purple_attachment_new(1337lu, "text/plain");
+
+	g_assert_true(purple_attachment_equal(attachment, attachment));
+
+	g_assert_finalize_object(attachment);
+}
+
+static void
+test_purple_attachment_equal_true(void) {
+	PurpleAttachment *attachment1 = NULL;
+	PurpleAttachment *attachment2 = NULL;
+
+	attachment1 = purple_attachment_new(1337lu, "text/plain");
+	attachment2 = purple_attachment_new(1337lu, "text/html");
+
+	g_assert_true(purple_attachment_equal(attachment1, attachment2));
+
+	g_assert_finalize_object(attachment1);
+	g_assert_finalize_object(attachment2);
+}
+
+static void
+test_purple_attachment_equal_false(void) {
+	PurpleAttachment *attachment1 = NULL;
+	PurpleAttachment *attachment2 = NULL;
+
+	attachment1 = purple_attachment_new(1337lu, "text/plain");
+	attachment2 = purple_attachment_new(7331lu, "text/html");
+
+	g_assert_false(purple_attachment_equal(attachment1, attachment2));
+
+	g_assert_finalize_object(attachment1);
+	g_assert_finalize_object(attachment2);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+int
+main(int argc, char *argv[]) {
+	g_test_init(&argc, &argv, NULL);
+	g_test_set_nonfatal_assertions();
+
+	g_test_add_func("/attachment/new", test_purple_attachment_new);
+	g_test_add_func("/attachment/properties",
+	                test_purple_attachment_properties);
+	g_test_add_func("/attachment/notify", test_purple_attachment_notify);
+
+	g_test_add_func("/attachment/equal/self",
+	                test_purple_attachment_equal_self);
+	g_test_add_func("/attachment/equal/true",
+	                test_purple_attachment_equal_true);
+	g_test_add_func("/attachment/equal/false",
+	                test_purple_attachment_equal_false);
+
+	return g_test_run();
+}
